Browse Source

Merge pull request #97 from JimBobSquarePants/IDisposable

Make all images implement IDisposable
pull/100/head
James Jackson-South 9 years ago
committed by GitHub
parent
commit
206664e990
  1. 2
      src/ImageSharp.Drawing/project.json
  2. 18
      src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs
  3. 2
      src/ImageSharp.Formats.Bmp/project.json
  4. 2
      src/ImageSharp.Formats.Gif/project.json
  5. 8
      src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
  6. 1
      src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
  7. 2
      src/ImageSharp.Formats.Jpeg/project.json
  8. 2
      src/ImageSharp.Formats.Png/project.json
  9. 109
      src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs
  10. 78
      src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs
  11. 83
      src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs
  12. 94
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  13. 127
      src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs
  14. 67
      src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs
  15. 141
      src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs
  16. 32
      src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs
  17. 26
      src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs
  18. 72
      src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs
  19. 141
      src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs
  20. 140
      src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs
  21. 35
      src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs
  22. 5
      src/ImageSharp.Processing/project.json
  23. 33
      src/ImageSharp/Image/IImageBase{TColor}.cs
  24. 130
      src/ImageSharp/Image/ImageBase{TColor}.cs
  25. 16
      src/ImageSharp/Image/Image{TColor}.cs
  26. 145
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  27. 42
      src/ImageSharp/Image/PixelPool{TColor}.cs
  28. 30
      src/ImageSharp/Quantizers/Quantize.cs
  29. 2
      src/ImageSharp/project.json
  30. 18
      tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
  31. 24
      tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
  32. 24
      tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
  33. 23
      tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs
  34. 29
      tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs
  35. 12
      tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs
  36. 34
      tests/ImageSharp.Benchmarks/Image/CopyPixels.cs
  37. 6
      tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs
  38. 17
      tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs
  39. 6
      tests/ImageSharp.Benchmarks/Image/DecodeGif.cs
  40. 6
      tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs
  41. 6
      tests/ImageSharp.Benchmarks/Image/DecodePng.cs
  42. 8
      tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
  43. 4
      tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs
  44. 8
      tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
  45. 8
      tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs
  46. 8
      tests/ImageSharp.Benchmarks/Image/EncodePng.cs
  47. 10
      tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs
  48. 8
      tests/ImageSharp.Benchmarks/Samplers/Crop.cs
  49. 6
      tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs
  50. 16
      tests/ImageSharp.Benchmarks/Samplers/Resize.cs
  51. 137
      tests/ImageSharp.Tests/Drawing/BeziersTests.cs
  52. 21
      tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
  53. 101
      tests/ImageSharp.Tests/Drawing/DrawPathTests.cs
  54. 71
      tests/ImageSharp.Tests/Drawing/FillPatternTests.cs
  55. 100
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs
  56. 226
      tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs
  57. 155
      tests/ImageSharp.Tests/Drawing/LineTests.cs
  58. 112
      tests/ImageSharp.Tests/Drawing/PolygonTests.cs
  59. 32
      tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs
  60. 102
      tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs
  61. 138
      tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs
  62. 156
      tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
  63. 15
      tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs
  64. 105
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  65. 16
      tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs
  66. 61
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  67. 44
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  68. 85
      tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs
  69. 4
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
  70. 20
      tests/ImageSharp.Tests/Formats/Png/PngTests.cs
  71. 9
      tests/ImageSharp.Tests/Image/ImageTests.cs
  72. 118
      tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
  73. 74
      tests/ImageSharp.Tests/Image/PixelPoolTests.cs
  74. 20
      tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs
  75. 19
      tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs
  76. 8
      tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs
  77. 10
      tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs
  78. 8
      tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs
  79. 10
      tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs
  80. 10
      tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs
  81. 10
      tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs
  82. 10
      tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs
  83. 8
      tests/ImageSharp.Tests/Processors/Filters/CropTest.cs
  84. 15
      tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs
  85. 10
      tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs
  86. 10
      tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs
  87. 10
      tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs
  88. 10
      tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs
  89. 31
      tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs
  90. 10
      tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs
  91. 10
      tests/ImageSharp.Tests/Processors/Filters/HueTest.cs
  92. 16
      tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs
  93. 8
      tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs
  94. 15
      tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs
  95. 21
      tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs
  96. 8
      tests/ImageSharp.Tests/Processors/Filters/PadTest.cs
  97. 8
      tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs
  98. 8
      tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs
  99. 76
      tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs
  100. 10
      tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs

2
src/ImageSharp.Drawing/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp.Drawing", "title": "ImageSharp.Drawing",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [

18
src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs

@ -34,7 +34,7 @@ namespace ImageSharp.Formats
/// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param> /// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param>
public void Encode<TColor>(ImageBase<TColor> image, Stream stream, BmpBitsPerPixel bitsPerPixel) public void Encode<TColor>(ImageBase<TColor> image, Stream stream, BmpBitsPerPixel bitsPerPixel)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -119,23 +119,23 @@ namespace ImageSharp.Formats
/// Writes the pixel data to the binary stream. /// Writes the pixel data to the binary stream.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="image"> /// <param name="image">
/// The <see cref="ImageBase{TColor}"/> containing pixel data. /// The <see cref="ImageBase{TColor}"/> containing pixel data.
/// </param> /// </param>
private void WriteImage<TColor>(EndianBinaryWriter writer, ImageBase<TColor> image) private void WriteImage<TColor>(EndianBinaryWriter writer, ImageBase<TColor> image)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
switch (this.bmpBitsPerPixel) switch (this.bmpBitsPerPixel)
{ {
case BmpBitsPerPixel.Pixel32: case BmpBitsPerPixel.Pixel32:
this.Write32Bit<TColor>(writer, pixels); this.Write32Bit(writer, pixels);
break; break;
case BmpBitsPerPixel.Pixel24: case BmpBitsPerPixel.Pixel24:
this.Write24Bit<TColor>(writer, pixels); this.Write24Bit(writer, pixels);
break; break;
} }
} }
@ -145,11 +145,11 @@ namespace ImageSharp.Formats
/// Writes the 32bit color palette to the stream. /// Writes the 32bit color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TColor}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="PixelAccessor{TColor}"/> containing pixel data.</param>
private void Write32Bit<TColor>(EndianBinaryWriter writer, PixelAccessor<TColor> pixels) private void Write32Bit<TColor>(EndianBinaryWriter writer, PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
using (PixelArea<TColor> row = new PixelArea<TColor>(pixels.Width, ComponentOrder.Zyxw, this.padding)) using (PixelArea<TColor> row = new PixelArea<TColor>(pixels.Width, ComponentOrder.Zyxw, this.padding))
{ {
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
@ -164,11 +164,11 @@ namespace ImageSharp.Formats
/// Writes the 24bit color palette to the stream. /// Writes the 24bit color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TColor}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="PixelAccessor{TColor}"/> containing pixel data.</param>
private void Write24Bit<TColor>(EndianBinaryWriter writer, PixelAccessor<TColor> pixels) private void Write24Bit<TColor>(EndianBinaryWriter writer, PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
using (PixelArea<TColor> row = new PixelArea<TColor>(pixels.Width, ComponentOrder.Zyx, this.padding)) using (PixelArea<TColor> row = new PixelArea<TColor>(pixels.Width, ComponentOrder.Zyx, this.padding))
{ {
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)

2
src/ImageSharp.Formats.Bmp/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp.Formats.Bmp", "title": "ImageSharp.Formats.Bmp",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [

2
src/ImageSharp.Formats.Gif/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp.Formats.Gif", "title": "ImageSharp.Formats.Gif",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [

8
src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs

@ -5,10 +5,8 @@
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System; using System;
using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using ImageSharp.Formats.Jpg; using ImageSharp.Formats.Jpg;
@ -586,7 +584,7 @@ namespace ImageSharp.Formats
byte yellow = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; byte yellow = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)];
TColor packed = default(TColor); TColor packed = default(TColor);
this.PackCmyk<TColor>(ref packed, cyan, magenta, yellow, x, y); this.PackCmyk(ref packed, cyan, magenta, yellow, x, y);
pixels[x, y] = packed; pixels[x, y] = packed;
} }
}); });
@ -744,7 +742,7 @@ namespace ImageSharp.Formats
byte cr = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; byte cr = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)];
TColor packed = default(TColor); TColor packed = default(TColor);
this.PackYcck<TColor>(ref packed, yy, cb, cr, x, y); this.PackYcck(ref packed, yy, cb, cr, x, y);
pixels[x, y] = packed; pixels[x, y] = packed;
} }
}); });
@ -1037,7 +1035,7 @@ namespace ImageSharp.Formats
} }
this.InputProcessor.ReadFull(this.Temp, 0, remaining); this.InputProcessor.ReadFull(this.Temp, 0, remaining);
this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1]; this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1];
} }
/// <summary> /// <summary>

1
src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs

@ -8,7 +8,6 @@ namespace ImageSharp.Formats
using System; using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using ImageSharp.Formats.Jpg; using ImageSharp.Formats.Jpg;

2
src/ImageSharp.Formats.Jpeg/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp.Formats.Jpeg", "title": "ImageSharp.Formats.Jpeg",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [

2
src/ImageSharp.Formats.Png/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp.Formats.Png", "title": "ImageSharp.Formats.Png",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [

109
src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -54,73 +54,74 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1; int maxY = endY - 1;
int maxX = endX - 1; int maxX = endX - 1;
TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(source.Width, source.Height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
startY,
endY,
this.ParallelOptions,
y =>
{ {
for (int x = startX; x < endX; x++) Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
{ {
float rX = 0; for (int x = startX; x < endX; x++)
float gX = 0;
float bX = 0;
float rY = 0;
float gY = 0;
float bY = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelYHeight; fy++)
{ {
int fyr = fy - radiusY; float rX = 0;
int offsetY = y + fyr; float gX = 0;
float bX = 0;
offsetY = offsetY.Clamp(0, maxY); float rY = 0;
float gY = 0;
for (int fx = 0; fx < kernelXWidth; fx++) float bY = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelYHeight; fy++)
{ {
int fxr = fx - radiusX; int fyr = fy - radiusY;
int offsetX = x + fxr; int offsetY = y + fyr;
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); offsetY = offsetY.Clamp(0, maxY);
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight)
{
rX += this.KernelX[fy][fx] * r;
gX += this.KernelX[fy][fx] * g;
bX += this.KernelX[fy][fx] * b;
}
if (fx < kernelYWidth) for (int fx = 0; fx < kernelXWidth; fx++)
{ {
rY += this.KernelY[fy][fx] * r; int fxr = fx - radiusX;
gY += this.KernelY[fy][fx] * g; int offsetX = x + fxr;
bY += this.KernelY[fy][fx] * b;
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight)
{
rX += this.KernelX[fy][fx] * r;
gX += this.KernelX[fy][fx] * g;
bX += this.KernelX[fy][fx] * b;
}
if (fx < kernelYWidth)
{
rY += this.KernelY[fy][fx] * r;
gY += this.KernelY[fy][fx] * g;
bY += this.KernelY[fy][fx] * b;
}
} }
} }
}
float red = (float)Math.Sqrt((rX * rX) + (rY * rY)); float red = (float)Math.Sqrt((rX * rX) + (rY * rY));
float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); float green = (float)Math.Sqrt((gX * gX) + (gY * gY));
float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); float blue = (float)Math.Sqrt((bX * bX) + (bY * bY));
TColor packed = default(TColor); TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed; targetPixels[x, y] = packed;
} }
}); });
} }
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

78
src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -45,13 +45,17 @@ namespace ImageSharp.Processing.Processors
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = new TColor[width * height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
TColor[] firstPass = new TColor[width * height]; {
using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, height))
this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX); using (PixelAccessor<TColor> sourcePixels = source.Lock())
this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY); {
this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX);
this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY);
}
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -60,13 +64,13 @@ namespace ImageSharp.Processing.Processors
/// </summary> /// </summary>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <param name="target">The target pixels to apply the process to.</param> /// <param name="targetPixels">The target pixels to apply the process to.</param>
/// <param name="source">The source pixels. Cannot be null.</param> /// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="sourceRectangle"> /// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param> /// </param>
/// <param name="kernel">The kernel operator.</param> /// <param name="kernel">The kernel operator.</param>
private void ApplyConvolution(int width, int height, TColor[] target, TColor[] source, Rectangle sourceRectangle, float[][] kernel) private void ApplyConvolution(int width, int height, PixelAccessor<TColor> targetPixels, PixelAccessor<TColor> sourcePixels, Rectangle sourceRectangle, float[][] kernel)
{ {
int kernelHeight = kernel.Length; int kernelHeight = kernel.Length;
int kernelWidth = kernel[0].Length; int kernelWidth = kernel[0].Length;
@ -80,45 +84,41 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1; int maxY = endY - 1;
int maxX = endX - 1; int maxX = endX - 1;
using (PixelAccessor<TColor> sourcePixels = source.Lock<TColor>(width, height)) Parallel.For(
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height)) startY,
endY,
this.ParallelOptions,
y =>
{ {
Parallel.For( for (int x = startX; x < endX; x++)
startY,
endY,
this.ParallelOptions,
y =>
{ {
for (int x = startX; x < endX; x++) Vector4 destination = default(Vector4);
{
Vector4 destination = default(Vector4);
// Apply each matrix multiplier to the color components for each pixel. // Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++) for (int fy = 0; fy < kernelHeight; fy++)
{ {
int fyr = fy - radiusY; int fyr = fy - radiusY;
int offsetY = y + fyr; int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY); offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelWidth; fx++) for (int fx = 0; fx < kernelWidth; fx++)
{ {
int fxr = fx - radiusX; int fxr = fx - radiusX;
int offsetX = x + fxr; int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy][fx] * currentColor; destination += kernel[fy][fx] * currentColor;
}
} }
TColor packed = default(TColor);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
} }
});
} TColor packed = default(TColor);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
}
});
} }
} }
} }

83
src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -44,60 +44,61 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1; int maxY = endY - 1;
int maxX = endX - 1; int maxX = endX - 1;
TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(source.Width, source.Height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
startY,
endY,
this.ParallelOptions,
y =>
{ {
for (int x = startX; x < endX; x++) Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
{ {
float rX = 0; for (int x = startX; x < endX; x++)
float gX = 0;
float bX = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelLength; fy++)
{ {
int fyr = fy - radius; float rX = 0;
int offsetY = y + fyr; float gX = 0;
float bX = 0;
offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelLength; fx++) // Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelLength; fy++)
{ {
int fxr = fx - radius; int fyr = fy - radius;
int offsetX = x + fxr; int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelLength; fx++)
{
int fxr = fx - radius;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X; float r = currentColor.X;
float g = currentColor.Y; float g = currentColor.Y;
float b = currentColor.Z; float b = currentColor.Z;
rX += kernelX[fy][fx] * r; rX += kernelX[fy][fx] * r;
gX += kernelX[fy][fx] * g; gX += kernelX[fy][fx] * g;
bX += kernelX[fy][fx] * b; bX += kernelX[fy][fx] * b;
}
} }
}
float red = rX; float red = rX;
float green = gX; float green = gX;
float blue = bX; float blue = bX;
TColor packed = default(TColor); TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed; targetPixels[x, y] = packed;
} }
}); });
} }
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

94
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs

@ -75,64 +75,62 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY); int maxY = Math.Min(source.Height, endY);
// First run. // we need a clean copy for each pass to start from
ImageBase<TColor> target = new Image<TColor>(source.Width, source.Height); using (ImageBase<TColor> cleanCopy = new Image<TColor>(source))
target.ClonePixels(source.Width, source.Height, source.Pixels);
new ConvolutionProcessor<TColor>(kernels[0]).Apply(target, sourceRectangle);
if (kernels.Length == 1)
{ {
return; new ConvolutionProcessor<TColor>(kernels[0]).Apply(source, sourceRectangle);
}
int shiftY = startY; if (kernels.Length == 1)
int shiftX = startX; {
return;
// Reset offset if necessary. }
if (minX > 0)
{
shiftX = 0;
}
if (minY > 0) int shiftY = startY;
{ int shiftX = startX;
shiftY = 0;
}
// Additional runs. // Reset offset if necessary.
// ReSharper disable once ForCanBeConvertedToForeach if (minX > 0)
for (int i = 1; i < kernels.Length; i++) {
{ shiftX = 0;
// Create a clone for each pass and copy the offset pixels across. }
ImageBase<TColor> pass = new Image<TColor>(source.Width, source.Height);
pass.ClonePixels(source.Width, source.Height, source.Pixels);
new ConvolutionProcessor<TColor>(kernels[i]).Apply(pass, sourceRectangle); if (minY > 0)
{
shiftY = 0;
}
using (PixelAccessor<TColor> passPixels = pass.Lock()) // Additional runs.
using (PixelAccessor<TColor> targetPixels = target.Lock()) // ReSharper disable once ForCanBeConvertedToForeach
for (int i = 1; i < kernels.Length; i++)
{ {
Parallel.For( using (ImageBase<TColor> pass = new Image<TColor>(cleanCopy))
minY, {
maxY, new ConvolutionProcessor<TColor>(kernels[i]).Apply(pass, sourceRectangle);
this.ParallelOptions,
y => using (PixelAccessor<TColor> passPixels = pass.Lock())
using (PixelAccessor<TColor> targetPixels = source.Lock())
{ {
int offsetY = y - shiftY; Parallel.For(
for (int x = minX; x < maxX; x++) minY,
{ maxY,
int offsetX = x - shiftX; this.ParallelOptions,
y =>
// Grab the max components of the two pixels {
TColor packed = default(TColor); int offsetY = y - shiftY;
packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4())); for (int x = minX; x < maxX; x++)
targetPixels[offsetX, offsetY] = packed; {
} int offsetX = x - shiftX;
});
// Grab the max components of the two pixels
TColor packed = default(TColor);
packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4()));
targetPixels[offsetX, offsetY] = packed;
}
});
}
}
} }
} }
source.SetPixels(source.Width, source.Height, target.Pixels);
} }
/// <inheritdoc/> /// <inheritdoc/>

127
src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs

@ -67,91 +67,92 @@ namespace ImageSharp.Processing.Processors
startX = 0; startX = 0;
} }
TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(source.Width, source.Height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
minY, {
maxY, Parallel.For(
this.ParallelOptions, minY,
y => maxY,
{ this.ParallelOptions,
for (int x = startX; x < endX; x++) y =>
{ {
int maxIntensity = 0; for (int x = startX; x < endX; x++)
int maxIndex = 0;
int[] intensityBin = new int[levels];
float[] redBin = new float[levels];
float[] blueBin = new float[levels];
float[] greenBin = new float[levels];
for (int fy = 0; fy <= radius; fy++)
{ {
int fyr = fy - radius; int maxIntensity = 0;
int offsetY = y + fyr; int maxIndex = 0;
// Skip the current row
if (offsetY < minY)
{
continue;
}
// Outwith the current bounds so break. int[] intensityBin = new int[levels];
if (offsetY >= maxY) float[] redBin = new float[levels];
{ float[] blueBin = new float[levels];
break; float[] greenBin = new float[levels];
}
for (int fx = 0; fx <= radius; fx++) for (int fy = 0; fy <= radius; fy++)
{ {
int fxr = fx - radius; int fyr = fy - radius;
int offsetX = x + fxr; int offsetY = y + fyr;
// Skip the column // Skip the current row
if (offsetX < 0) if (offsetY < minY)
{ {
continue; continue;
} }
if (offsetX < maxX) // Outwith the current bounds so break.
if (offsetY >= maxY)
{ {
// ReSharper disable once AccessToDisposedClosure break;
Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); }
float sourceRed = color.X;
float sourceBlue = color.Z;
float sourceGreen = color.Y;
int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); for (int fx = 0; fx <= radius; fx++)
{
int fxr = fx - radius;
int offsetX = x + fxr;
intensityBin[currentIntensity] += 1; // Skip the column
blueBin[currentIntensity] += sourceBlue; if (offsetX < 0)
greenBin[currentIntensity] += sourceGreen; {
redBin[currentIntensity] += sourceRed; continue;
}
if (intensityBin[currentIntensity] > maxIntensity) if (offsetX < maxX)
{ {
maxIntensity = intensityBin[currentIntensity]; // ReSharper disable once AccessToDisposedClosure
maxIndex = currentIntensity; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
float sourceRed = color.X;
float sourceBlue = color.Z;
float sourceGreen = color.Y;
int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1));
intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += sourceBlue;
greenBin[currentIntensity] += sourceGreen;
redBin[currentIntensity] += sourceRed;
if (intensityBin[currentIntensity] > maxIntensity)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
}
} }
} }
}
float red = Math.Abs(redBin[maxIndex] / maxIntensity); float red = Math.Abs(redBin[maxIndex] / maxIntensity);
float green = Math.Abs(greenBin[maxIndex] / maxIntensity); float green = Math.Abs(greenBin[maxIndex] / maxIntensity);
float blue = Math.Abs(blueBin[maxIndex] / maxIntensity); float blue = Math.Abs(blueBin[maxIndex] / maxIntensity);
TColor packed = default(TColor); TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed; targetPixels[x, y] = packed;
}
} }
} });
}); }
}
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

67
src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs

@ -63,51 +63,52 @@ namespace ImageSharp.Processing.Processors
// Get the range on the y-plane to choose from. // Get the range on the y-plane to choose from.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size);
TColor[] target = new TColor[source.Width * source.Height];
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(source.Width, source.Height))
{ {
Parallel.ForEach( using (PixelAccessor<TColor> sourcePixels = source.Lock())
range, {
this.ParallelOptions, Parallel.ForEach(
y => range,
{ this.ParallelOptions,
int offsetY = y - startY; y =>
int offsetPy = offset;
for (int x = minX; x < maxX; x += size)
{ {
int offsetX = x - startX; int offsetY = y - startY;
int offsetPx = offset; int offsetPy = offset;
// Make sure that the offset is within the boundary of the image. for (int x = minX; x < maxX; x += size)
while (offsetY + offsetPy >= maxY)
{ {
offsetPy--; int offsetX = x - startX;
} int offsetPx = offset;
while (x + offsetPx >= maxX) // Make sure that the offset is within the boundary of the image.
{ while (offsetY + offsetPy >= maxY)
offsetPx--; {
} offsetPy--;
}
// Get the pixel color in the centre of the soon to be pixelated area. while (x + offsetPx >= maxX)
// ReSharper disable AccessToDisposedClosure {
TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; offsetPx--;
}
// For each pixel in the pixelate size, set it to the centre color. // Get the pixel color in the centre of the soon to be pixelated area.
for (int l = offsetY; l < offsetY + size && l < maxY; l++) // ReSharper disable AccessToDisposedClosure
{ TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy];
for (int k = offsetX; k < offsetX + size && k < maxX; k++)
// For each pixel in the pixelate size, set it to the centre color.
for (int l = offsetY; l < offsetY + size && l < maxY; l++)
{ {
targetPixels[k, l] = pixel; for (int k = offsetX; k < offsetX + size && k < maxX; k++)
{
targetPixels[k, l] = pixel;
}
} }
} }
} });
});
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

141
src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs

@ -66,103 +66,104 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(height, endY); int maxY = Math.Min(height, endY);
TColor[] target = new TColor[width * height];
if (this.Sampler is NearestNeighborResampler) if (this.Sampler is NearestNeighborResampler)
{ {
// Scaling factors // Scaling factors
float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
minY, {
maxY, Parallel.For(
this.ParallelOptions, minY,
y => maxY,
{ this.ParallelOptions,
// Y coordinates of source points y =>
int originY = (int)((y - startY) * heightFactor);
for (int x = minX; x < maxX; x++)
{ {
// X coordinates of source points // Y coordinates of source points
targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; int originY = (int)((y - startY) * heightFactor);
}
}); for (int x = minX; x < maxX; x++)
{
// X coordinates of source points
targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY];
}
});
}
// Break out now.
source.SwapPixelsBuffers(targetPixels);
return;
} }
// Break out now.
source.SetPixels(width, height, target);
return;
} }
// Interpolate the image using the calculated weights. // Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY // First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle. // are the upper and lower bounds of the source rectangle.
TColor[] firstPass = new TColor[width * source.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> firstPassPixels = firstPass.Lock<TColor>(width, source.Height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, source.Height))
sourceRectangle.Height, {
this.ParallelOptions, Parallel.For(
y => 0,
{ sourceRectangle.Height,
for (int x = minX; x < maxX; x++) this.ParallelOptions,
y =>
{ {
// Ensure offsets are normalised for cropping and padding. for (int x = minX; x < maxX; x++)
Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; {
// Ensure offsets are normalised for cropping and padding.
Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values;
// Destination color components // Destination color components
Vector4 destination = Vector4.Zero; Vector4 destination = Vector4.Zero;
for (int i = 0; i < horizontalValues.Length; i++) for (int i = 0; i < horizontalValues.Length; i++)
{ {
Weight xw = horizontalValues[i]; Weight xw = horizontalValues[i];
destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value;
} }
TColor d = default(TColor); TColor d = default(TColor);
d.PackFromVector4(destination.Compress()); d.PackFromVector4(destination.Compress());
firstPassPixels[x, y] = d; firstPassPixels[x, y] = d;
} }
}); });
// Now process the rows.
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
// Ensure offsets are normalised for cropping and padding.
Weight[] verticalValues = this.VerticalWeights[y - startY].Values;
for (int x = 0; x < width; x++) // Now process the rows.
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{ {
// Destination color components // Ensure offsets are normalised for cropping and padding.
Vector4 destination = Vector4.Zero; Weight[] verticalValues = this.VerticalWeights[y - startY].Values;
for (int i = 0; i < verticalValues.Length; i++) for (int x = 0; x < width; x++)
{ {
Weight yw = verticalValues[i]; // Destination color components
destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; Vector4 destination = Vector4.Zero;
for (int i = 0; i < verticalValues.Length; i++)
{
Weight yw = verticalValues[i];
destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value;
}
TColor d = default(TColor);
d.PackFromVector4(destination.Compress());
targetPixels[x, y] = d;
} }
});
}
TColor d = default(TColor); source.SwapPixelsBuffers(targetPixels);
d.PackFromVector4(destination.Compress());
targetPixels[x, y] = d;
}
});
} }
source.SetPixels(width, height, target);
} }
} }
} }

32
src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs

@ -42,25 +42,25 @@ namespace ImageSharp.Processing.Processors
int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X);
int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right);
TColor[] target = new TColor[this.CropRectangle.Width * this.CropRectangle.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(this.CropRectangle.Width, this.CropRectangle.Height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(this.CropRectangle.Width, this.CropRectangle.Height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
minY, {
maxY, Parallel.For(
this.ParallelOptions, minY,
y => maxY,
{ this.ParallelOptions,
for (int x = minX; x < maxX; x++) y =>
{ {
targetPixels[x - minX, y - minY] = sourcePixels[x, y]; for (int x = minX; x < maxX; x++)
} {
}); targetPixels[x - minX, y - minY] = sourcePixels[x, y];
} }
});
}
source.SetPixels(this.CropRectangle.Width, this.CropRectangle.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

26
src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs

@ -36,24 +36,24 @@ namespace ImageSharp.Processing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{ {
ImageBase<TColor> temp = new Image<TColor>(source.Width, source.Height); using (ImageBase<TColor> temp = new Image<TColor>(source))
temp.ClonePixels(source.Width, source.Height, source.Pixels); {
// Detect the edges.
new SobelProcessor<TColor>().Apply(temp, sourceRectangle);
// Detect the edges. // Apply threshold binarization filter.
new SobelProcessor<TColor>().Apply(temp, sourceRectangle); new BinaryThresholdProcessor<TColor>(this.Value).Apply(temp, sourceRectangle);
// Apply threshold binarization filter. // Search for the first white pixels
new BinaryThresholdProcessor<TColor>(this.Value).Apply(temp, sourceRectangle); Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
// Search for the first white pixels if (rectangle == sourceRectangle)
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); {
return;
}
if (rectangle == sourceRectangle) new CropProcessor<TColor>(rectangle).Apply(source, sourceRectangle);
{
return;
} }
new CropProcessor<TColor>(rectangle).Apply(source, sourceRectangle);
} }
} }
} }

72
src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs

@ -55,27 +55,27 @@ namespace ImageSharp.Processing.Processors
int height = source.Height; int height = source.Height;
int halfHeight = (int)Math.Ceiling(source.Height * .5F); int halfHeight = (int)Math.Ceiling(source.Height * .5F);
TColor[] target = new TColor[width * height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
halfHeight, Parallel.For(
this.ParallelOptions, 0,
y => halfHeight,
{ this.ParallelOptions,
for (int x = 0; x < width; x++) y =>
{ {
int newY = height - y - 1; for (int x = 0; x < width; x++)
targetPixels[x, y] = sourcePixels[x, newY]; {
targetPixels[x, newY] = sourcePixels[x, y]; int newY = height - y - 1;
} targetPixels[x, y] = sourcePixels[x, newY];
}); targetPixels[x, newY] = sourcePixels[x, y];
} }
});
}
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -89,27 +89,27 @@ namespace ImageSharp.Processing.Processors
int height = source.Height; int height = source.Height;
int halfWidth = (int)Math.Ceiling(width * .5F); int halfWidth = (int)Math.Ceiling(width * .5F);
TColor[] target = new TColor[width * height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
height, Parallel.For(
this.ParallelOptions, 0,
y => height,
{ this.ParallelOptions,
for (int x = 0; x < halfWidth; x++) y =>
{ {
int newX = width - x - 1; for (int x = 0; x < halfWidth; x++)
targetPixels[x, y] = sourcePixels[newX, y]; {
targetPixels[newX, y] = sourcePixels[x, y]; int newX = width - x - 1;
} targetPixels[x, y] = sourcePixels[newX, y];
}); targetPixels[newX, y] = sourcePixels[x, y];
} }
});
}
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

141
src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs

@ -65,103 +65,104 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(height, endY); int maxY = Math.Min(height, endY);
TColor[] target = new TColor[width * height];
if (this.Sampler is NearestNeighborResampler) if (this.Sampler is NearestNeighborResampler)
{ {
// Scaling factors // Scaling factors
float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
minY, {
maxY, Parallel.For(
this.ParallelOptions, minY,
y => maxY,
{ this.ParallelOptions,
// Y coordinates of source points y =>
int originY = (int)((y - startY) * heightFactor);
for (int x = minX; x < maxX; x++)
{ {
// X coordinates of source points // Y coordinates of source points
targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; int originY = (int)((y - startY) * heightFactor);
}
}); for (int x = minX; x < maxX; x++)
{
// X coordinates of source points
targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY];
}
});
}
// Break out now.
source.SwapPixelsBuffers(targetPixels);
return;
} }
// Break out now.
source.SetPixels(width, height, target);
return;
} }
// Interpolate the image using the calculated weights. // Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY // First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle. // are the upper and lower bounds of the source rectangle.
TColor[] firstPass = new TColor[width * source.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> firstPassPixels = firstPass.Lock<TColor>(width, source.Height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, source.Height))
sourceRectangle.Height, {
this.ParallelOptions, Parallel.For(
y => 0,
{ sourceRectangle.Height,
for (int x = minX; x < maxX; x++) this.ParallelOptions,
y =>
{ {
// Ensure offsets are normalised for cropping and padding. for (int x = minX; x < maxX; x++)
Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; {
// Ensure offsets are normalised for cropping and padding.
Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values;
// Destination color components // Destination color components
Vector4 destination = Vector4.Zero; Vector4 destination = Vector4.Zero;
for (int i = 0; i < horizontalValues.Length; i++) for (int i = 0; i < horizontalValues.Length; i++)
{ {
Weight xw = horizontalValues[i]; Weight xw = horizontalValues[i];
destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value; destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value;
} }
TColor d = default(TColor); TColor d = default(TColor);
d.PackFromVector4(destination); d.PackFromVector4(destination);
firstPassPixels[x, y] = d; firstPassPixels[x, y] = d;
} }
}); });
// Now process the rows.
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
// Ensure offsets are normalised for cropping and padding.
Weight[] verticalValues = this.VerticalWeights[y - startY].Values;
for (int x = 0; x < width; x++) // Now process the rows.
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{ {
// Destination color components // Ensure offsets are normalised for cropping and padding.
Vector4 destination = Vector4.Zero; Weight[] verticalValues = this.VerticalWeights[y - startY].Values;
for (int i = 0; i < verticalValues.Length; i++) for (int x = 0; x < width; x++)
{ {
Weight yw = verticalValues[i]; // Destination color components
destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value; Vector4 destination = Vector4.Zero;
for (int i = 0; i < verticalValues.Length; i++)
{
Weight yw = verticalValues[i];
destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value;
}
TColor d = default(TColor);
d.PackFromVector4(destination);
targetPixels[x, y] = d;
} }
});
}
TColor d = default(TColor); source.SwapPixelsBuffers(targetPixels);
d.PackFromVector4(destination);
targetPixels[x, y] = d;
}
});
} }
source.SetPixels(width, height, target);
} }
} }
} }

140
src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs

@ -42,29 +42,30 @@ namespace ImageSharp.Processing.Processors
int height = this.CanvasRectangle.Height; int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width; int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
height, Parallel.For(
this.ParallelOptions, 0,
y => height,
{ this.ParallelOptions,
for (int x = 0; x < width; x++) y =>
{ {
Point transformedPoint = Point.Rotate(new Point(x, y), matrix); for (int x = 0; x < width; x++)
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{ {
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; Point transformedPoint = Point.Rotate(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
} }
} });
}); }
}
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -124,28 +125,29 @@ namespace ImageSharp.Processing.Processors
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(height, width))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(height, width))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
height, Parallel.For(
this.ParallelOptions, 0,
y => height,
{ this.ParallelOptions,
for (int x = 0; x < width; x++) y =>
{ {
int newX = height - y - 1; for (int x = 0; x < width; x++)
newX = height - newX - 1; {
int newY = width - x - 1; int newX = height - y - 1;
targetPixels[newX, newY] = sourcePixels[x, y]; newX = height - newX - 1;
} int newY = width - x - 1;
}); targetPixels[newX, newY] = sourcePixels[x, y];
} }
});
}
source.SetPixels(height, width, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -156,27 +158,28 @@ namespace ImageSharp.Processing.Processors
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
height, Parallel.For(
this.ParallelOptions, 0,
y => height,
{ this.ParallelOptions,
for (int x = 0; x < width; x++) y =>
{ {
int newX = width - x - 1; for (int x = 0; x < width; x++)
int newY = height - y - 1; {
targetPixels[newX, newY] = sourcePixels[x, y]; int newX = width - x - 1;
} int newY = height - y - 1;
}); targetPixels[newX, newY] = sourcePixels[x, y];
} }
});
}
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -187,26 +190,27 @@ namespace ImageSharp.Processing.Processors
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(height, width))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(height, width))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
height, Parallel.For(
this.ParallelOptions, 0,
y => height,
{ this.ParallelOptions,
for (int x = 0; x < width; x++) y =>
{ {
int newX = height - y - 1; for (int x = 0; x < width; x++)
targetPixels[newX, x] = sourcePixels[x, y]; {
} int newX = height - y - 1;
}); targetPixels[newX, x] = sourcePixels[x, y];
} }
});
}
source.SetPixels(height, width, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

35
src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs

@ -42,29 +42,30 @@ namespace ImageSharp.Processing.Processors
int height = this.CanvasRectangle.Height; int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width; int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(width, height))
{ {
Parallel.For( using (PixelAccessor<TColor> sourcePixels = source.Lock())
0, {
height, Parallel.For(
this.ParallelOptions, 0,
y => height,
{ this.ParallelOptions,
for (int x = 0; x < width; x++) y =>
{ {
Point transformedPoint = Point.Skew(new Point(x, y), matrix); for (int x = 0; x < width; x++)
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{ {
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; Point transformedPoint = Point.Skew(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
} }
} });
}); }
}
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <inheritdoc/> /// <inheritdoc/>

5
src/ImageSharp.Processing/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp.Processing", "title": "ImageSharp.Processing",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [
@ -39,8 +39,7 @@
}, },
"dependencies": { "dependencies": {
"ImageSharp": { "ImageSharp": {
"target": "project", "target": "project"
"version": "1.0.0-alpha1"
}, },
"StyleCop.Analyzers": { "StyleCop.Analyzers": {
"version": "1.1.0-beta001", "version": "1.1.0-beta001",

33
src/ImageSharp/Image/IImageBase{TColor}.cs

@ -11,11 +11,13 @@ namespace ImageSharp
/// Encapsulates the basic properties and methods required to manipulate images in varying formats. /// Encapsulates the basic properties and methods required to manipulate images in varying formats.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
public interface IImageBase<TColor> : IImageBase public interface IImageBase<TColor> : IImageBase, IDisposable
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
/// <summary> /// <summary>
/// Gets the pixels as an array of the given packed pixel format. /// Gets the pixels as an array of the given packed pixel format.
/// Important. Due to the nature in the way this is constructed do not rely on the length
/// of the array for calculations. Use Width * Height.
/// </summary> /// </summary>
TColor[] Pixels { get; } TColor[] Pixels { get; }
@ -29,35 +31,6 @@ namespace ImageSharp
/// </exception> /// </exception>
void InitPixels(int width, int height); void InitPixels(int width, int height);
/// <summary>
/// Sets the pixel array of the image to the given value.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">The array with pixels. Must be a multiple of the width and height.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
void SetPixels(int width, int height, TColor[] pixels);
/// <summary>
/// Sets the pixel array of the image to the given value, creating a copy of
/// the original pixels.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">The array with pixels. Must be a multiple of four times the width and height.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
void ClonePixels(int width, int height, TColor[] pixels);
/// <summary> /// <summary>
/// Locks the image providing access to the pixels. /// Locks the image providing access to the pixels.
/// <remarks> /// <remarks>

130
src/ImageSharp/Image/ImageBase{TColor}.cs

@ -22,6 +22,17 @@ namespace ImageSharp
/// </summary> /// </summary>
private TColor[] pixelBuffer; private TColor[] pixelBuffer;
/// <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>
private bool isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TColor}"/> class. /// Initializes a new instance of the <see cref="ImageBase{TColor}"/> class.
/// </summary> /// </summary>
@ -41,7 +52,7 @@ namespace ImageSharp
/// <param name="configuration"> /// <param name="configuration">
/// The configuration providing initialization code which allows extending the library. /// The configuration providing initialization code which allows extending the library.
/// </param> /// </param>
/// <exception cref="System.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>
protected ImageBase(int width, int height, Configuration configuration = null) protected ImageBase(int width, int height, Configuration configuration = null)
@ -67,11 +78,12 @@ namespace ImageSharp
this.Height = other.Height; this.Height = other.Height;
this.CopyProperties(other); this.CopyProperties(other);
// Copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. // Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here.
this.pixelBuffer = new TColor[this.Width * this.Height]; this.RentPixels();
using (PixelAccessor<TColor> sourcePixels = other.Lock()) using (PixelAccessor<TColor> sourcePixels = other.Lock())
using (PixelAccessor<TColor> target = this.Lock()) using (PixelAccessor<TColor> target = this.Lock())
{ {
// Check we can do this without crashing
sourcePixels.CopyTo(target); sourcePixels.CopyTo(target);
} }
} }
@ -108,58 +120,53 @@ namespace ImageSharp
/// </summary> /// </summary>
public Configuration Configuration { get; private set; } public Configuration Configuration { get; private set; }
/// <inheritdoc/> /// <inheritdoc />
public void InitPixels(int width, int height) public void Dispose()
{ {
Guard.MustBeGreaterThan(width, 0, nameof(width)); this.Dispose(true);
Guard.MustBeGreaterThan(height, 0, nameof(height));
// This object will be cleaned up by the Dispose method.
this.Width = width; // Therefore, you should call GC.SuppressFinalize to
this.Height = height; // take this object off the finalization queue
this.pixelBuffer = new TColor[width * height]; // and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
public void SetPixels(int width, int height, TColor[] pixels) public void InitPixels(int width, int height)
{ {
Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(pixels, nameof(pixels));
if (pixels.Length != width * height)
{
throw new ArgumentException("Pixel array must have the length of Width * Height.");
}
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.pixelBuffer = pixels; this.RentPixels();
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ClonePixels(int width, int height, TColor[] pixels) public virtual PixelAccessor<TColor> Lock()
{ {
Guard.MustBeGreaterThan(width, 0, nameof(width)); return new PixelAccessor<TColor>(this);
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(pixels, nameof(pixels));
if (pixels.Length != width * height)
{
throw new ArgumentException("Pixel array must have the length of Width * Height.");
}
this.Width = width;
this.Height = height;
// Copy the pixels. TODO: use Unsafe.Copy.
this.pixelBuffer = new TColor[pixels.Length];
Array.Copy(pixels, this.pixelBuffer, pixels.Length);
} }
/// <inheritdoc/> /// <summary>
public virtual PixelAccessor<TColor> Lock() /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer.
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(PixelAccessor<TColor> pixelSource)
{ {
return new PixelAccessor<TColor>(this); Guard.NotNull(pixelSource, nameof(pixelSource));
Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory");
int newWidth = pixelSource.Width;
int newHeight = pixelSource.Height;
// Push my memory into the accessor (which in turn unpins the old puffer ready for the images use)
TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true);
this.Width = newWidth;
this.Height = newHeight;
this.pixelBuffer = newPixels;
} }
/// <summary> /// <summary>
@ -174,5 +181,52 @@ namespace ImageSharp
this.Quality = other.Quality; this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay; this.FrameDelay = other.FrameDelay;
} }
/// <summary>
/// Releases any unmanaged resources from the inheriting class.
/// </summary>
protected virtual void ReleaseUnmanagedResources()
{
// TODO release unmanaged resources here
}
/// <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;
}
this.ReleaseUnmanagedResources();
if (disposing)
{
this.ReturnPixels();
}
// Note disposing is done.
this.isDisposed = true;
}
/// <summary>
/// Rents the pixel array from the pool.
/// </summary>
private void RentPixels()
{
this.pixelBuffer = PixelPool<TColor>.RentPixels(this.Width * this.Height);
}
/// <summary>
/// Returns the rented pixel array back to the pool.
/// </summary>
private void ReturnPixels()
{
PixelPool<TColor>.ReturnPixels(this.pixelBuffer);
this.pixelBuffer = null;
}
} }
} }

16
src/ImageSharp/Image/Image{TColor}.cs

@ -334,9 +334,9 @@ namespace ImageSharp
target.ExifProfile = new ExifProfile(this.ExifProfile); target.ExifProfile = new ExifProfile(this.ExifProfile);
} }
foreach (ImageFrame<TColor> frame in this.Frames) for (int i = 0; i < this.Frames.Count; i++)
{ {
target.Frames.Add(frame.To<TColor2>()); target.Frames.Add(this.Frames[i].To<TColor2>());
} }
return target; return target;
@ -372,6 +372,18 @@ namespace ImageSharp
return new ImageFrame<TColor>(this); return new ImageFrame<TColor>(this);
} }
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < this.Frames.Count; i++)
{
this.Frames[i].Dispose();
}
base.Dispose(disposing);
}
/// <summary> /// <summary>
/// Loads the image from the given stream. /// Loads the image from the given stream.
/// </summary> /// </summary>

145
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -44,6 +44,11 @@ namespace ImageSharp
/// </remarks> /// </remarks>
private bool isDisposed; private bool isDisposed;
/// <summary>
/// The pixel buffer
/// </summary>
private TColor[] pixelBuffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class. /// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
/// </summary> /// </summary>
@ -54,40 +59,51 @@ namespace ImageSharp
Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Width, 0, "image width");
Guard.MustBeGreaterThan(image.Height, 0, "image height"); Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.Width = image.Width; this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels, false);
this.Height = image.Height;
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
this.PixelSize = Unsafe.SizeOf<TColor>();
this.RowStride = this.Width * this.PixelSize;
this.ParallelOptions = image.Configuration.ParallelOptions; this.ParallelOptions = image.Configuration.ParallelOptions;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class. /// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
/// </summary> /// </summary>
/// <param name="width">Gets the width of the image represented by the pixel buffer.</param> /// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param> /// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param> /// <param name="pixels">The pixel buffer.</param>
public PixelAccessor(int width, int height, TColor[] pixels) public PixelAccessor(int width, int height, TColor[] pixels)
: this(width, height, pixels, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
/// </summary>
/// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
public PixelAccessor(int width, int height)
: this(width, height, PixelPool<TColor>.RentPixels(width * height), true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}" /> class.
/// </summary>
/// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param>
/// <param name="pooledMemory">if set to <c>true</c> then the <see cref="T:TColor[]"/> is from the <see cref="PixelPool{TColor}"/> thus should be returned once disposed.</param>
private PixelAccessor(int width, int height, TColor[] pixels, bool pooledMemory)
{ {
Guard.NotNull(pixels, nameof(pixels)); Guard.NotNull(pixels, nameof(pixels));
Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.MustBeGreaterThan(height, 0, nameof(height));
if (pixels.Length != width * height) if (!(pixels.Length >= width * height))
{ {
throw new ArgumentException("Pixel array must have the length of Width * Height."); throw new ArgumentException($"Pixel array must have the length of at least {width * height}.");
} }
this.Width = width; this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory);
this.Height = height;
this.pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
this.PixelSize = Unsafe.SizeOf<TColor>();
this.RowStride = this.Width * this.PixelSize;
this.ParallelOptions = Configuration.Default.ParallelOptions; this.ParallelOptions = Configuration.Default.ParallelOptions;
} }
@ -99,6 +115,11 @@ namespace ImageSharp
this.Dispose(); this.Dispose();
} }
/// <summary>
/// Gets a value indicating whether the current pixel buffer is from a pooled source.
/// </summary>
public bool PooledMemory { get; private set; }
/// <summary> /// <summary>
/// Gets the pointer to the pixel buffer. /// Gets the pointer to the pixel buffer.
/// </summary> /// </summary>
@ -107,22 +128,22 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Gets the size of a single pixel in the number of bytes. /// Gets the size of a single pixel in the number of bytes.
/// </summary> /// </summary>
public int PixelSize { get; } public int PixelSize { get; private set; }
/// <summary> /// <summary>
/// Gets the width of one row in the number of bytes. /// Gets the width of one row in the number of bytes.
/// </summary> /// </summary>
public int RowStride { get; } public int RowStride { get; private set; }
/// <summary> /// <summary>
/// Gets the width of the image. /// Gets the width of the image.
/// </summary> /// </summary>
public int Width { get; } public int Width { get; private set; }
/// <summary> /// <summary>
/// Gets the height of the image. /// Gets the height of the image.
/// </summary> /// </summary>
public int Height { get; } public int Height { get; private set; }
/// <summary> /// <summary>
/// Gets the global parallel options for processing tasks in parallel. /// Gets the global parallel options for processing tasks in parallel.
@ -221,13 +242,7 @@ namespace ImageSharp
return; return;
} }
if (this.pixelsHandle.IsAllocated) this.UnPinPixels();
{
this.pixelsHandle.Free();
}
this.dataPointer = IntPtr.Zero;
this.pixelsBase = null;
// Note disposing is done. // Note disposing is done.
this.isDisposed = true; this.isDisposed = true;
@ -238,6 +253,12 @@ namespace ImageSharp
// and prevent finalization code for this object // and prevent finalization code for this object
// from executing a second time. // from executing a second time.
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
if (this.PooledMemory)
{
PixelPool<TColor>.ReturnPixels(this.pixelBuffer);
this.pixelBuffer = null;
}
} }
/// <summary> /// <summary>
@ -248,6 +269,22 @@ namespace ImageSharp
Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height)); Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height));
} }
/// <summary>
/// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!!
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixels.</param>
/// <param name="pooledMemory">If set to <c>true</c> this indicates that the pixel buffer is from a pooled source.</param>
/// <returns>Returns the old pixel data thats has gust been replaced.</returns>
/// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelPool.ReturnPixels()"/> is called.</remarks>
internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory)
{
TColor[] oldPixels = this.pixelBuffer;
this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory);
return oldPixels;
}
/// <summary> /// <summary>
/// Copies the pixels to another <see cref="PixelAccessor{TColor}"/> of the same size. /// Copies the pixels to another <see cref="PixelAccessor{TColor}"/> of the same size.
/// </summary> /// </summary>
@ -472,6 +509,54 @@ namespace ImageSharp
return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>()); return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>());
} }
/// <summary>
/// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!!
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixels.</param>
/// <param name="pooledMemory">If set to <c>true</c> this indicates that the pixel buffer is from a pooled source.</param>
private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels, bool pooledMemory)
{
this.pixelBuffer = pixels;
this.PooledMemory = pooledMemory;
this.Width = width;
this.Height = height;
this.PinPixels();
this.PixelSize = Unsafe.SizeOf<TColor>();
this.RowStride = this.Width * this.PixelSize;
}
/// <summary>
/// Pins the pixels data.
/// </summary>
private void PinPixels()
{
// unpin any old pixels just incase
this.UnPinPixels();
this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
}
/// <summary>
/// Unpins pixels data.
/// </summary>
private void UnPinPixels()
{
if (this.pixelsBase != null)
{
if (this.pixelsHandle.IsAllocated)
{
this.pixelsHandle.Free();
}
this.dataPointer = IntPtr.Zero;
this.pixelsBase = null;
}
}
/// <summary> /// <summary>
/// Copy an area of pixels to the image. /// Copy an area of pixels to the image.
/// </summary> /// </summary>
@ -551,13 +636,13 @@ namespace ImageSharp
int width = Math.Min(area.Width, this.Width - x); int width = Math.Min(area.Width, this.Width - x);
if (width < 1) if (width < 1)
{ {
throw new ArgumentOutOfRangeException(nameof(width), width, $"Invalid area size specified."); throw new ArgumentOutOfRangeException(nameof(width), width, "Invalid area size specified.");
} }
int height = Math.Min(area.Height, this.Height - y); int height = Math.Min(area.Height, this.Height - y);
if (height < 1) if (height < 1)
{ {
throw new ArgumentOutOfRangeException(nameof(height), height, $"Invalid area size specified."); throw new ArgumentOutOfRangeException(nameof(height), height, "Invalid area size specified.");
} }
} }

42
src/ImageSharp/Image/PixelPool{TColor}.cs

@ -0,0 +1,42 @@
// <copyright file="PixelPool{TColor}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Buffers;
/// <summary>
/// Provides a resource pool that enables reusing instances of type <see cref="T:TColor[]"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public static class PixelPool<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// The <see cref="ArrayPool{T}"/> used to pool data. TODO: Choose sensible default size and count
/// </summary>
private static readonly ArrayPool<TColor> ArrayPool = ArrayPool<TColor>.Create(int.MaxValue, 50);
/// <summary>
/// Rents the pixel array from the pool.
/// </summary>
/// <param name="minimumLength">The minimum length of the array to return.</param>
/// <returns>The <see cref="T:TColor[]"/></returns>
public static TColor[] RentPixels(int minimumLength)
{
return ArrayPool.Rent(minimumLength);
}
/// <summary>
/// Returns the rented pixel array back to the pool.
/// </summary>
/// <param name="array">The array to return to the buffer pool.</param>
public static void ReturnPixels(TColor[] array)
{
ArrayPool.Return(array, true);
}
}
}

30
src/ImageSharp/Quantizers/Quantize.cs

@ -60,20 +60,26 @@ namespace ImageSharp
int pixelCount = quantized.Pixels.Length; int pixelCount = quantized.Pixels.Length;
int palleteCount = quantized.Palette.Length - 1; int palleteCount = quantized.Palette.Length - 1;
TColor[] pixels = new TColor[pixelCount];
Parallel.For( using (PixelAccessor<TColor> pixels = new PixelAccessor<TColor>(quantized.Width, quantized.Height))
0, {
pixelCount, Parallel.For(
source.Configuration.ParallelOptions, 0,
i => pixels.Height,
{ source.Configuration.ParallelOptions,
TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; y =>
pixels[i] = color; {
}); for (var x = 0; x < pixels.Width; x++)
{
var i = x + (y * pixels.Width);
TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])];
pixels[x, y] = color;
}
});
source.SetPixels(source.Width, source.Height, pixels); source.SwapPixelsBuffers(pixels);
return source; return source;
}
} }
} }
} }

2
src/ImageSharp/project.json

@ -1,5 +1,5 @@
{ {
"version": "1.0.0-alpha1-*", "version": "1.0.0-alpha2-*",
"title": "ImageSharp", "title": "ImageSharp",
"description": "A cross-platform library for the processing of image files; written in C#", "description": "A cross-platform library for the processing of image files; written in C#",
"authors": [ "authors": [

18
tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs

@ -47,18 +47,22 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Beziers")] [Benchmark(Description = "ImageSharp Draw Beziers")]
public void DrawLinesCore() public void DrawLinesCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
{
image.DrawBeziers(CoreColor.HotPink, 10, new[] { image.DrawBeziers(
CoreColor.HotPink,
10,
new[] {
new Vector2(10, 500), new Vector2(10, 500),
new Vector2(30, 10), new Vector2(30, 10),
new Vector2(240, 30), new Vector2(240, 30),
new Vector2(300, 500) new Vector2(300, 500)
}); });
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(ms);
}
} }
} }
} }

24
tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs

@ -46,17 +46,21 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Lines")] [Benchmark(Description = "ImageSharp Draw Lines")]
public void DrawLinesCore() public void DrawLinesCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
image.DrawLines(CoreColor.HotPink, 10, new[] {
new Vector2(10, 10),
new Vector2(550, 50),
new Vector2(200, 400)
});
using (MemoryStream ms = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.DrawLines(
CoreColor.HotPink,
10,
new[] {
new Vector2(10, 10),
new Vector2(550, 50),
new Vector2(200, 400)
});
using (MemoryStream ms = new MemoryStream())
{
image.SaveAsBmp(ms);
}
} }
} }
} }

24
tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs

@ -45,17 +45,21 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Polygon")] [Benchmark(Description = "ImageSharp Draw Polygon")]
public void DrawPolygonCore() public void DrawPolygonCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
image.DrawPolygon(CoreColor.HotPink, 10, new[] {
new Vector2(10, 10),
new Vector2(550, 50),
new Vector2(200, 400)
});
using (MemoryStream ms = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.DrawPolygon(
CoreColor.HotPink,
10,
new[] {
new Vector2(10, 10),
new Vector2(550, 50),
new Vector2(200, 400)
});
using (MemoryStream ms = new MemoryStream())
{
image.SaveAsBmp(ms);
}
} }
} }
} }

23
tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs

@ -44,17 +44,20 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill Polygon")] [Benchmark(Description = "ImageSharp Fill Polygon")]
public void DrawSolidPolygonCore() public void DrawSolidPolygonCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
image.FillPolygon(CoreColor.HotPink,
new[] {
new Vector2(10, 10),
new Vector2(550, 50),
new Vector2(200, 400)
});
using (MemoryStream ms = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.FillPolygon(
CoreColor.HotPink,
new[] {
new Vector2(10, 10),
new Vector2(550, 50),
new Vector2(200, 400)
});
using (MemoryStream ms = new MemoryStream())
{
image.SaveAsBmp(ms);
}
} }
} }
} }

29
tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs

@ -28,7 +28,6 @@ namespace ImageSharp.Benchmarks
{ {
graphics.InterpolationMode = InterpolationMode.Default; graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.SmoothingMode = SmoothingMode.AntiAlias;
var pen = new Pen(Color.HotPink, 10);
graphics.FillRectangle(Brushes.HotPink, new Rectangle(10, 10, 190, 140)); graphics.FillRectangle(Brushes.HotPink, new Rectangle(10, 10, 190, 140));
} }
return destination.Size; return destination.Size;
@ -38,25 +37,29 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill Rectangle")] [Benchmark(Description = "ImageSharp Fill Rectangle")]
public CoreSize FillRactangleCore() public CoreSize FillRactangleCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
{
image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140))); image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140)));
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);
}
} }
[Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")] [Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")]
public CoreSize FillPolygonCore() public CoreSize FillPolygonCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
{
image.FillPolygon(CoreColor.HotPink, new[] { image.FillPolygon(
new Vector2(10, 10), CoreColor.HotPink,
new Vector2(200, 10), new[] {
new Vector2(200, 150), new Vector2(10, 10),
new Vector2(10, 150) }); new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150) });
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

12
tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs

@ -38,12 +38,14 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill with Pattern")] [Benchmark(Description = "ImageSharp Fill with Pattern")]
public void DrawPatternPolygon3Core() public void DrawPatternPolygon3Core()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
image.Fill(CoreBrushes.BackwardDiagonal(CoreColor.HotPink));
using (MemoryStream ms = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.Fill(CoreBrushes.BackwardDiagonal(CoreColor.HotPink));
using (MemoryStream ms = new MemoryStream())
{
image.SaveAsBmp(ms);
}
} }
} }
} }

34
tests/ImageSharp.Benchmarks/Image/CopyPixels.cs

@ -17,24 +17,26 @@ namespace ImageSharp.Benchmarks.Image
[Benchmark(Description = "Copy by Pixel")] [Benchmark(Description = "Copy by Pixel")]
public CoreColor CopyByPixel() public CoreColor CopyByPixel()
{ {
CoreImage source = new CoreImage(1024, 768); using (CoreImage source = new CoreImage(1024, 768))
CoreImage target = new CoreImage(1024, 768); using (CoreImage target = new CoreImage(1024, 768))
using (PixelAccessor<CoreColor> sourcePixels = source.Lock())
using (PixelAccessor<CoreColor> targetPixels = target.Lock())
{ {
Parallel.For( using (PixelAccessor<CoreColor> sourcePixels = source.Lock())
0, using (PixelAccessor<CoreColor> targetPixels = target.Lock())
source.Height, {
Configuration.Default.ParallelOptions, Parallel.For(
y => 0,
{ source.Height,
for (int x = 0; x < source.Width; x++) Configuration.Default.ParallelOptions,
{ y =>
targetPixels[x, y] = sourcePixels[x, y]; {
} for (int x = 0; x < source.Width; x++)
}); {
targetPixels[x, y] = sourcePixels[x, y];
}
});
return targetPixels[0, 0]; return targetPixels[0, 0];
}
} }
} }
} }

6
tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs

@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image
{ {
using (MemoryStream memoryStream = new MemoryStream(this.bmpBytes)) using (MemoryStream memoryStream = new MemoryStream(this.bmpBytes))
{ {
CoreImage image = new CoreImage(memoryStream); using (CoreImage image = new CoreImage(memoryStream))
return new CoreSize(image.Width, image.Height); {
return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

17
tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs

@ -29,37 +29,40 @@ namespace ImageSharp.Benchmarks.Image
this.filter4 = new MemoryStream(File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Png/filter4.png")); this.filter4 = new MemoryStream(File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Png/filter4.png"));
} }
private Image LoadPng(MemoryStream stream) private Size LoadPng(MemoryStream stream)
{ {
return new Image(stream); using (Image image = new Image(stream))
{
return new Size(image.Width, image.Height);
}
} }
[Benchmark(Baseline = true, Description = "None-filtered PNG file")] [Benchmark(Baseline = true, Description = "None-filtered PNG file")]
public Image PngFilter0() public Size PngFilter0()
{ {
return LoadPng(filter0); return LoadPng(filter0);
} }
[Benchmark(Description = "Sub-filtered PNG file")] [Benchmark(Description = "Sub-filtered PNG file")]
public Image PngFilter1() public Size PngFilter1()
{ {
return LoadPng(filter1); return LoadPng(filter1);
} }
[Benchmark(Description = "Up-filtered PNG file")] [Benchmark(Description = "Up-filtered PNG file")]
public Image PngFilter2() public Size PngFilter2()
{ {
return LoadPng(filter2); return LoadPng(filter2);
} }
[Benchmark(Description = "Average-filtered PNG file")] [Benchmark(Description = "Average-filtered PNG file")]
public Image PngFilter3() public Size PngFilter3()
{ {
return LoadPng(filter3); return LoadPng(filter3);
} }
[Benchmark(Description = "Paeth-filtered PNG file")] [Benchmark(Description = "Paeth-filtered PNG file")]
public Image PngFilter4() public Size PngFilter4()
{ {
return LoadPng(filter4); return LoadPng(filter4);
} }

6
tests/ImageSharp.Benchmarks/Image/DecodeGif.cs

@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image
{ {
using (MemoryStream memoryStream = new MemoryStream(this.gifBytes)) using (MemoryStream memoryStream = new MemoryStream(this.gifBytes))
{ {
CoreImage image = new CoreImage(memoryStream); using (CoreImage image = new CoreImage(memoryStream))
return new CoreSize(image.Width, image.Height); {
return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

6
tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs

@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image
{ {
using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes))
{ {
CoreImage image = new CoreImage(memoryStream); using (CoreImage image = new CoreImage(memoryStream))
return new CoreSize(image.Width, image.Height); {
return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

6
tests/ImageSharp.Benchmarks/Image/DecodePng.cs

@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image
{ {
using (MemoryStream memoryStream = new MemoryStream(this.pngBytes)) using (MemoryStream memoryStream = new MemoryStream(this.pngBytes))
{ {
CoreImage image = new CoreImage(memoryStream); using (CoreImage image = new CoreImage(memoryStream))
return new CoreSize(image.Width, image.Height); {
return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

8
tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs

@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image
} }
} }
[Cleanup]
public void Cleanup()
{
this.bmpStream.Dispose();
this.bmpCore.Dispose();
this.bmpDrawing.Dispose();
}
[Benchmark(Baseline = true, Description = "System.Drawing Bmp")] [Benchmark(Baseline = true, Description = "System.Drawing Bmp")]
public void BmpSystemDrawing() public void BmpSystemDrawing()
{ {

4
tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs

@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" };
[Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] [Benchmark(Description = "EncodeBmpMultiple - ImageSharp")]
public void EncodeGifImageSharp() public void EncodeBmpImageSharp()
{ {
this.ForEachImageSharpImage( this.ForEachImageSharpImage(
(img, ms) => (img, ms) =>
@ -29,7 +29,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")] [Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")]
public void EncodeGifSystemDrawing() public void EncodeBmpSystemDrawing()
{ {
this.ForEachSystemDrawingImage( this.ForEachSystemDrawingImage(
(img, ms) => (img, ms) =>

8
tests/ImageSharp.Benchmarks/Image/EncodeGif.cs

@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image
} }
} }
[Cleanup]
public void Cleanup()
{
this.bmpStream.Dispose();
this.bmpCore.Dispose();
this.bmpDrawing.Dispose();
}
[Benchmark(Baseline = true, Description = "System.Drawing Gif")] [Benchmark(Baseline = true, Description = "System.Drawing Gif")]
public void GifSystemDrawing() public void GifSystemDrawing()
{ {

8
tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs

@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image
} }
} }
[Cleanup]
public void Cleanup()
{
this.bmpStream.Dispose();
this.bmpCore.Dispose();
this.bmpDrawing.Dispose();
}
[Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")]
public void JpegSystemDrawing() public void JpegSystemDrawing()
{ {

8
tests/ImageSharp.Benchmarks/Image/EncodePng.cs

@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image
} }
} }
[Cleanup]
public void Cleanup()
{
this.bmpStream.Dispose();
this.bmpCore.Dispose();
this.bmpDrawing.Dispose();
}
[Benchmark(Baseline = true, Description = "System.Drawing Png")] [Benchmark(Baseline = true, Description = "System.Drawing Png")]
public void PngSystemDrawing() public void PngSystemDrawing()
{ {

10
tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs

@ -28,11 +28,13 @@ namespace ImageSharp.Benchmarks.Image
[Benchmark(Description = "ImageSharp GetSet pixel")] [Benchmark(Description = "ImageSharp GetSet pixel")]
public CoreColor ResizeCore() public CoreColor ResizeCore()
{ {
CoreImage image = new CoreImage(400, 400); using (CoreImage image = new CoreImage(400, 400))
using (PixelAccessor<CoreColor> imagePixels = image.Lock())
{ {
imagePixels[200, 200] = CoreColor.White; using (PixelAccessor<CoreColor> imagePixels = image.Lock())
return imagePixels[200, 200]; {
imagePixels[200, 200] = CoreColor.White;
return imagePixels[200, 200];
}
} }
} }
} }

8
tests/ImageSharp.Benchmarks/Samplers/Crop.cs

@ -38,9 +38,11 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Crop")] [Benchmark(Description = "ImageSharp Crop")]
public CoreSize CropResizeCore() public CoreSize CropResizeCore()
{ {
CoreImage image = new CoreImage(800, 800); using (CoreImage image = new CoreImage(800, 800))
image.Crop(100, 100); {
return new CoreSize(image.Width, image.Height); image.Crop(100, 100);
return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

6
tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs

@ -28,6 +28,12 @@ namespace ImageSharp.Benchmarks
} }
} }
[Cleanup]
public void Cleanup()
{
this.image.Dispose();
}
[Benchmark(Description = "ImageSharp DetectEdges")] [Benchmark(Description = "ImageSharp DetectEdges")]
public void ImageProcessorCoreDetectEdges() public void ImageProcessorCoreDetectEdges()
{ {

16
tests/ImageSharp.Benchmarks/Samplers/Resize.cs

@ -37,17 +37,21 @@ namespace ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Resize")] [Benchmark(Description = "ImageSharp Resize")]
public CoreSize ResizeCore() public CoreSize ResizeCore()
{ {
CoreImage image = new CoreImage(2000, 2000); using (CoreImage image = new CoreImage(2000, 2000))
image.Resize(400, 400); {
return new CoreSize(image.Width, image.Height); image.Resize(400, 400);
return new CoreSize(image.Width, image.Height);
}
} }
[Benchmark(Description = "ImageSharp Compand Resize")] [Benchmark(Description = "ImageSharp Compand Resize")]
public CoreSize ResizeCoreCompand() public CoreSize ResizeCoreCompand()
{ {
CoreImage image = new CoreImage(2000, 2000); using (CoreImage image = new CoreImage(2000, 2000))
image.Resize(400, 400, true); {
return new CoreSize(image.Width, image.Height); image.Resize(400, 400, true);
return new CoreSize(image.Width, image.Height);
}
} }
} }
} }

137
tests/ImageSharp.Tests/Drawing/BeziersTests.cs

@ -15,89 +15,88 @@ namespace ImageSharp.Tests.Drawing
public class Beziers : FileTestBase public class Beziers : FileTestBase
{ {
[Fact] [Fact]
public void ImageShouldBeOverlayedByBezierLine() public void ImageShouldBeOverlayedByBezierLine()
{ {
string path = CreateOutputDirectory("Drawing","BezierLine"); string path = this.CreateOutputDirectory("Drawing", "BezierLine");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawBeziers(Color.HotPink, 5, new[] {
new Vector2(10, 400),
new Vector2(30, 10),
new Vector2(240, 30),
new Vector2(300, 400)
})
.Save(output);
}
using (var sourcePixels = image.Lock())
{ {
//top of curve using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
Assert.Equal(Color.HotPink, sourcePixels[138,115]); {
image.BackgroundColor(Color.Blue)
//start points .DrawBeziers(Color.HotPink, 5,
Assert.Equal(Color.HotPink, sourcePixels[10, 400]); new[] {
Assert.Equal(Color.HotPink, sourcePixels[300, 400]); new Vector2(10, 400),
new Vector2(30, 10),
//curve points should not be never be set new Vector2(240, 30),
Assert.Equal(Color.Blue, sourcePixels[30, 10]); new Vector2(300, 400)
Assert.Equal(Color.Blue, sourcePixels[240, 30]); })
.Save(output);
// inside shape should be empty }
Assert.Equal(Color.Blue, sourcePixels[200, 250]);
using (PixelAccessor<Color> sourcePixels = image.Lock())
{
//top of curve
Assert.Equal(Color.HotPink, sourcePixels[138, 115]);
//start points
Assert.Equal(Color.HotPink, sourcePixels[10, 400]);
Assert.Equal(Color.HotPink, sourcePixels[300, 400]);
//curve points should not be never be set
Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Color.Blue, sourcePixels[200, 250]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedBezierLineWithOpacity() public void ImageShouldBeOverlayedBezierLineWithOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "BezierLine"); string path = this.CreateOutputDirectory("Drawing", "BezierLine");
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.DrawBeziers(color, 10, new[] { image.BackgroundColor(Color.Blue)
new Vector2(10, 400), .DrawBeziers(color,
new Vector2(30, 10), 10,
new Vector2(240, 30), new[] {
new Vector2(300, 400) new Vector2(10, 400),
}) new Vector2(30, 10),
.Save(output); new Vector2(240, 30),
new Vector2(300, 400)
})
.Save(output);
}
//shift background color towards forground color by the opacity amount
Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Color> sourcePixels = image.Lock())
{
//top of curve
Assert.Equal(mergedColor, sourcePixels[138, 115]);
//start points
Assert.Equal(mergedColor, sourcePixels[10, 400]);
Assert.Equal(mergedColor, sourcePixels[300, 400]);
//curve points should not be never be set
Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Color.Blue, sourcePixels[200, 250]);
}
} }
}
//shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f));
using (var sourcePixels = image.Lock())
{
//top of curve
Assert.Equal(mergedColor, sourcePixels[138, 115]);
//start points
Assert.Equal(mergedColor, sourcePixels[10, 400]);
Assert.Equal(mergedColor, sourcePixels[300, 400]);
//curve points should not be never be set
Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Color.Blue, sourcePixels[200, 250]);
}
}
} }
} }

21
tests/ImageSharp.Tests/Drawing/DrawImageTest.cs

@ -6,7 +6,6 @@
namespace ImageSharp.Tests namespace ImageSharp.Tests
{ {
using System.IO; using System.IO;
using System.Linq;
using Xunit; using Xunit;
@ -15,18 +14,20 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyDrawImageFilter() public void ImageShouldApplyDrawImageFilter()
{ {
string path = CreateOutputDirectory("Drawing", "DrawImage"); string path = this.CreateOutputDirectory("Drawing", "DrawImage");
Image blend = TestFile.Create(TestImages.Bmp.Car).CreateImage(); using (Image blend = TestFile.Create(TestImages.Bmp.Car).CreateImage())
foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); foreach (TestFile file in Files)
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) using (Image image = file.CreateImage())
.Save(output); {
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{
image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))
.Save(output);
}
}
} }
} }
} }

101
tests/ImageSharp.Tests/Drawing/DrawPathTests.cs

@ -20,81 +20,82 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPath() public void ImageShouldBeOverlayedByPath()
{ {
string path = CreateOutputDirectory("Drawing", "Path"); string path = this.CreateOutputDirectory("Drawing", "Path");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
var linerSegemnt = new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300));
var bazierSegment = new BezierLineSegment(new Vector2(50, 300),
new Vector2(500, 500),
new Vector2(60, 10),
new Vector2(10, 400));
var p = new CorePath(linerSegemnt, bazierSegment);
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawPath(Color.HotPink, 5, p)
.Save(output);
}
using (var sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[9, 9]); LinearLineSegment linerSegemnt = new LinearLineSegment(
new Vector2(10, 10),
Assert.Equal(Color.HotPink, sourcePixels[199, 149]); new Vector2(200, 150),
new Vector2(50, 300));
Assert.Equal(Color.Blue, sourcePixels[50, 50]); BezierLineSegment bazierSegment = new BezierLineSegment(new Vector2(50, 300),
new Vector2(500, 500),
new Vector2(60, 10),
new Vector2(10, 400));
CorePath p = new CorePath(linerSegemnt, bazierSegment);
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
.BackgroundColor(Color.Blue)
.DrawPath(Color.HotPink, 5, p)
.Save(output);
}
using (PixelAccessor<Color> sourcePixels = image.Lock())
{
Assert.Equal(Color.HotPink, sourcePixels[9, 9]);
Assert.Equal(Color.HotPink, sourcePixels[199, 149]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedPathWithOpacity() public void ImageShouldBeOverlayedPathWithOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "Path"); string path = this.CreateOutputDirectory("Drawing", "Path");
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var linerSegemnt = new LinearLineSegment( LinearLineSegment linerSegemnt = new LinearLineSegment(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
); );
var bazierSegment = new BezierLineSegment(new Vector2(50, 300),
BezierLineSegment bazierSegment = new BezierLineSegment(new Vector2(50, 300),
new Vector2(500, 500), new Vector2(500, 500),
new Vector2(60, 10), new Vector2(60, 10),
new Vector2(10, 400)); new Vector2(10, 400));
var p = new CorePath(linerSegemnt, bazierSegment); CorePath p = new CorePath(linerSegemnt, bazierSegment);
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.DrawPath(color, 10, p) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPath(color, 10, p)
.Save(output);
}
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[9, 9]);
Assert.Equal(mergedColor, sourcePixels[199, 149]); Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
}
} }
} }

71
tests/ImageSharp.Tests/Drawing/FillPatternTests.cs

@ -15,55 +15,56 @@ namespace ImageSharp.Tests.Drawing
public class FillPatternBrushTests : FileTestBase public class FillPatternBrushTests : FileTestBase
{ {
private Image Test(string name, Color background, IBrush<Color> brush, Color[,] expectedPattern) private void Test(string name, Color background, IBrush<Color> brush, Color[,] expectedPattern)
{ {
string path = CreateOutputDirectory("Fill", "PatternBrush"); string path = this.CreateOutputDirectory("Fill", "PatternBrush");
Image image = new Image(20, 20); using (Image image = new Image(20, 20))
image
.Fill(background)
.Fill(brush);
using (FileStream output = File.OpenWrite($"{path}/{name}.png"))
{
image.Save(output);
}
using (var sourcePixels = image.Lock())
{ {
// lets pick random spots to start checking image
var r = new Random(); .Fill(background)
var xStride = expectedPattern.GetLength(1); .Fill(brush);
var yStride = expectedPattern.GetLength(0);
var offsetX = r.Next(image.Width / xStride) * xStride; using (FileStream output = File.OpenWrite($"{path}/{name}.png"))
var offsetY = r.Next(image.Height / yStride) * yStride;
for (var x = 0; x < xStride; x++)
{ {
for (var y = 0; y < yStride; y++) image.Save(output);
}
using (PixelAccessor<Color> sourcePixels = image.Lock())
{
// lets pick random spots to start checking
Random r = new Random();
int xStride = expectedPattern.GetLength(1);
int yStride = expectedPattern.GetLength(0);
int offsetX = r.Next(image.Width / xStride) * xStride;
int offsetY = r.Next(image.Height / yStride) * yStride;
for (int x = 0; x < xStride; x++)
{ {
var actualX = x + offsetX; for (int y = 0; y < yStride; y++)
var actualY = y + offsetY;
var expected = expectedPattern[y, x]; // inverted pattern
var actual = sourcePixels[actualX, actualY];
if (expected != actual)
{ {
Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})"); int actualX = x + offsetX;
int actualY = y + offsetY;
Color expected = expectedPattern[y, x]; // inverted pattern
Color actual = sourcePixels[actualX, actualY];
if (expected != actual)
{
Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})");
}
} }
} }
} }
using (FileStream output = File.OpenWrite($"{path}/{name}x4.png"))
{
image.Resize(80, 80).Save(output);
}
} }
using (FileStream output = File.OpenWrite($"{path}/{name}x4.png"))
{
image.Resize(80, 80).Save(output);
}
return image;
} }
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithPercent10() public void ImageShouldBeFloodFilledWithPercent10()
{ {
Test("Percent10", Color.Blue, Brushes.Percent10(Color.HotPink, Color.LimeGreen), new Color[,] { this.Test("Percent10", Color.Blue, Brushes.Percent10(Color.HotPink, Color.LimeGreen),
new[,]
{
{ Color.HotPink , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, { Color.HotPink , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen},
{ Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen},
{ Color.LimeGreen, Color.LimeGreen, Color.HotPink , Color.LimeGreen}, { Color.LimeGreen, Color.LimeGreen, Color.HotPink , Color.LimeGreen},

100
tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs

@ -18,71 +18,73 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithColorOnDefaultBackground() public void ImageShouldBeFloodFilledWithColorOnDefaultBackground()
{ {
string path = CreateOutputDirectory("Fill", "SolidBrush"); string path = this.CreateOutputDirectory("Fill", "SolidBrush");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/DefaultBack.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/DefaultBack.png"))
.Fill(Color.HotPink) {
.Save(output); image
} .Fill(Color.HotPink)
.Save(output);
using (var sourcePixels = image.Lock()) }
{
Assert.Equal(Color.HotPink, sourcePixels[9, 9]); using (PixelAccessor<Color> sourcePixels = image.Lock())
{
Assert.Equal(Color.HotPink, sourcePixels[199, 149]); Assert.Equal(Color.HotPink, sourcePixels[9, 9]);
Assert.Equal(Color.HotPink, sourcePixels[199, 149]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithColor() public void ImageShouldBeFloodFilledWithColor()
{ {
string path = CreateOutputDirectory("Fill", "SolidBrush"); string path = this.CreateOutputDirectory("Fill", "SolidBrush");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.Fill(Color.HotPink) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(Color.HotPink)
.Save(output);
using (var sourcePixels = image.Lock()) }
{
Assert.Equal(Color.HotPink, sourcePixels[9, 9]); using (PixelAccessor<Color> sourcePixels = image.Lock())
{
Assert.Equal(Color.HotPink, sourcePixels[199, 149]); Assert.Equal(Color.HotPink, sourcePixels[9, 9]);
Assert.Equal(Color.HotPink, sourcePixels[199, 149]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithColorOpacity() public void ImageShouldBeFloodFilledWithColorOpacity()
{ {
string path = CreateOutputDirectory("Fill", "SolidBrush"); string path = this.CreateOutputDirectory("Fill", "SolidBrush");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{
image
.BackgroundColor(Color.Blue)
.Fill(color)
.Save(output);
}
//shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[9, 9]); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{
image
.BackgroundColor(Color.Blue)
.Fill(color)
.Save(output);
}
//shift background color towards forground color by the opacity amount
Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (PixelAccessor<Color> sourcePixels = image.Lock())
{
Assert.Equal(mergedColor, sourcePixels[9, 9]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
}
} }
} }
} }

226
tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs

@ -5,12 +5,9 @@
namespace ImageSharp.Tests.Drawing namespace ImageSharp.Tests.Drawing
{ {
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using Xunit; using Xunit;
using Drawing;
using ImageSharp.Drawing;
using System.Numerics; using System.Numerics;
using ImageSharp.Drawing.Shapes; using ImageSharp.Drawing.Shapes;
using ImageSharp.Drawing.Pens; using ImageSharp.Drawing.Pens;
@ -20,98 +17,100 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPolygonOutline() public void ImageShouldBeOverlayedByPolygonOutline()
{ {
string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(93, 85), new Vector2(93, 85),
new Vector2(65, 137)); new Vector2(65, 137));
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[10, 10]); Assert.Equal(Color.HotPink, sourcePixels[10, 10]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 300]); Assert.Equal(Color.HotPink, sourcePixels[50, 300]);
Assert.Equal(Color.HotPink, sourcePixels[37, 85]); Assert.Equal(Color.HotPink, sourcePixels[37, 85]);
Assert.Equal(Color.HotPink, sourcePixels[93, 85]); Assert.Equal(Color.HotPink, sourcePixels[93, 85]);
Assert.Equal(Color.HotPink, sourcePixels[65, 137]); Assert.Equal(Color.HotPink, sourcePixels[65, 137]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole //inside hole
Assert.Equal(Color.Blue, sourcePixels[57, 99]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
//inside shape //inside shape
Assert.Equal(Color.Blue, sourcePixels[100, 192]); Assert.Equal(Color.Blue, sourcePixels[100, 192]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping() public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping()
{ {
string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(207, 25), new Vector2(207, 25),
new Vector2(263, 25), new Vector2(263, 25),
new Vector2(235, 57)); new Vector2(235, 57));
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/SimpleVanishHole.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/SimpleVanishHole.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[10, 10]); Assert.Equal(Color.HotPink, sourcePixels[10, 10]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 300]); Assert.Equal(Color.HotPink, sourcePixels[50, 300]);
//Assert.Equal(Color.HotPink, sourcePixels[37, 85]); //Assert.Equal(Color.HotPink, sourcePixels[37, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[93, 85]); //Assert.Equal(Color.HotPink, sourcePixels[93, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[65, 137]); //Assert.Equal(Color.HotPink, sourcePixels[65, 137]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole //inside hole
Assert.Equal(Color.Blue, sourcePixels[57, 99]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
//inside shape //inside shape
Assert.Equal(Color.Blue, sourcePixels[100, 192]); Assert.Equal(Color.Blue, sourcePixels[100, 192]);
}
} }
} }
@ -119,44 +118,45 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPolygonOutlineOverlapping() public void ImageShouldBeOverlayedByPolygonOutlineOverlapping()
{ {
string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(130, 40), new Vector2(130, 40),
new Vector2(65, 137)); new Vector2(65, 137));
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[10, 10]); Assert.Equal(Color.HotPink, sourcePixels[10, 10]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 300]); Assert.Equal(Color.HotPink, sourcePixels[50, 300]);
Assert.Equal(Color.Blue, sourcePixels[130, 41]); Assert.Equal(Color.Blue, sourcePixels[130, 41]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole Assert.Equal(Color.Blue, sourcePixels[2, 2]);
Assert.Equal(Color.Blue, sourcePixels[57, 99]);
//inside shape //inside hole
Assert.Equal(Color.Blue, sourcePixels[100, 192]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Color.Blue, sourcePixels[100, 192]);
}
} }
} }
@ -164,25 +164,26 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPolygonOutlineDashed() public void ImageShouldBeOverlayedByPolygonOutlineDashed()
{ {
string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(93, 85), new Vector2(93, 85),
new Vector2(65, 137)); new Vector2(65, 137));
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Dashed.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Dashed.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(Pens.Dash(Color.HotPink, 5), new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
.DrawPolygon(Pens.Dash(Color.HotPink, 5), new ComplexPolygon(simplePath, hole1))
.Save(output);
}
} }
} }
@ -190,54 +191,55 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(93, 85), new Vector2(93, 85),
new Vector2(65, 137)); new Vector2(65, 137));
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(color, 5, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPolygon(color, 5, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[10, 10]); Assert.Equal(mergedColor, sourcePixels[10, 10]);
Assert.Equal(mergedColor, sourcePixels[200, 150]); Assert.Equal(mergedColor, sourcePixels[200, 150]);
Assert.Equal(mergedColor, sourcePixels[50, 300]); Assert.Equal(mergedColor, sourcePixels[50, 300]);
Assert.Equal(mergedColor, sourcePixels[37, 85]); Assert.Equal(mergedColor, sourcePixels[37, 85]);
Assert.Equal(mergedColor, sourcePixels[93, 85]); Assert.Equal(mergedColor, sourcePixels[93, 85]);
Assert.Equal(mergedColor, sourcePixels[65, 137]); Assert.Equal(mergedColor, sourcePixels[65, 137]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole //inside hole
Assert.Equal(Color.Blue, sourcePixels[57, 99]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
//inside shape //inside shape
Assert.Equal(Color.Blue, sourcePixels[100, 192]); Assert.Equal(Color.Blue, sourcePixels[100, 192]);
}
} }
} }
} }

155
tests/ImageSharp.Tests/Drawing/LineTests.cs

@ -5,11 +5,9 @@
namespace ImageSharp.Tests.Drawing namespace ImageSharp.Tests.Drawing
{ {
using Drawing;
using ImageSharp.Drawing; using ImageSharp.Drawing;
using ImageSharp.Drawing.Pens; using ImageSharp.Drawing.Pens;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Numerics; using System.Numerics;
using Xunit; using Xunit;
@ -19,123 +17,132 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPath() public void ImageShouldBeOverlayedByPath()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.DrawLines(Color.HotPink, 5, new[] { image
.BackgroundColor(Color.Blue)
.DrawLines(Color.HotPink, 5,
new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}) })
.Save(output); .Save(output);
} }
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[9, 9]); Assert.Equal(Color.HotPink, sourcePixels[9, 9]);
Assert.Equal(Color.HotPink, sourcePixels[199, 149]); Assert.Equal(Color.HotPink, sourcePixels[199, 149]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByPath_NoAntialias() public void ImageShouldBeOverlayedByPath_NoAntialias()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple_noantialias.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple_noantialias.png"))
.BackgroundColor(Color.Blue) {
.DrawLines(Color.HotPink, 5, new[] { image
.BackgroundColor(Color.Blue)
.DrawLines(Color.HotPink, 5,
new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}, new GraphicsOptions(false)) },
.Save(output); new GraphicsOptions(false))
} .Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[9, 9]); Assert.Equal(Color.HotPink, sourcePixels[9, 9]);
Assert.Equal(Color.HotPink, sourcePixels[199, 149]); Assert.Equal(Color.HotPink, sourcePixels[199, 149]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByPathDashed() public void ImageShouldBeOverlayedByPathDashed()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Dashed.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Dashed.png"))
.BackgroundColor(Color.Blue) {
.DrawLines(Pens.Dash(Color.HotPink, 5), new[] { image
.BackgroundColor(Color.Blue)
.DrawLines(Pens.Dash(Color.HotPink, 5),
new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}) })
.Save(output); .Save(output);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByPathDotted() public void ImageShouldBeOverlayedByPathDotted()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Dot.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Dot.png"))
.BackgroundColor(Color.Blue) {
.DrawLines(Pens.Dot(Color.HotPink, 5), new[] { image
.BackgroundColor(Color.Blue)
.DrawLines(Pens.Dot(Color.HotPink, 5),
new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}) })
.Save(output); .Save(output);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByPathDashDot() public void ImageShouldBeOverlayedByPathDashDot()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/DashDot.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/DashDot.png"))
.BackgroundColor(Color.Blue) {
.DrawLines(Pens.DashDot(Color.HotPink, 5), new[] { image
.BackgroundColor(Color.Blue)
.DrawLines(Pens.DashDot(Color.HotPink, 5),
new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}) })
.Save(output); .Save(output);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByPathDashDotDot() public void ImageShouldBeOverlayedByPathDashDotDot()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); Image image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/DashDotDot.png")) using (FileStream output = File.OpenWrite($"{path}/DashDotDot.png"))
{ {
@ -153,12 +160,12 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedPathWithOpacity() public void ImageShouldBeOverlayedPathWithOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
Image image = new Image(500, 500);
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{ {
@ -173,9 +180,9 @@ namespace ImageSharp.Tests.Drawing
} }
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[9, 9]);
@ -188,9 +195,9 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPathOutline() public void ImageShouldBeOverlayedByPathOutline()
{ {
string path = CreateOutputDirectory("Drawing", "Lines"); string path = this.CreateOutputDirectory("Drawing", "Lines");
var image = new Image(500, 500); Image image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) using (FileStream output = File.OpenWrite($"{path}/Rectangle.png"))
{ {
@ -205,7 +212,7 @@ namespace ImageSharp.Tests.Drawing
.Save(output); .Save(output);
} }
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[8, 8]); Assert.Equal(Color.HotPink, sourcePixels[8, 8]);
@ -216,6 +223,6 @@ namespace ImageSharp.Tests.Drawing
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
} }
} }
} }
} }

112
tests/ImageSharp.Tests/Drawing/PolygonTests.cs

@ -18,97 +18,101 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPolygonOutline() public void ImageShouldBeOverlayedByPolygonOutline()
{ {
string path = CreateOutputDirectory("Drawing", "Polygons"); string path = this.CreateOutputDirectory("Drawing", "Polygons");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(Color.HotPink, 5, new[] { image
new Vector2(10, 10), .BackgroundColor(Color.Blue)
new Vector2(200, 150), .DrawPolygon(Color.HotPink, 5,
new Vector2(50, 300) new[] {
}) new Vector2(10, 10),
.Save(output); new Vector2(200, 150),
} new Vector2(50, 300)
})
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[9, 9]); Assert.Equal(Color.HotPink, sourcePixels[9, 9]);
Assert.Equal(Color.HotPink, sourcePixels[199, 149]); Assert.Equal(Color.HotPink, sourcePixels[199, 149]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "Polygons"); string path = this.CreateOutputDirectory("Drawing", "Polygons");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}; };
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(color, 10, simplePath) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPolygon(color, 10, simplePath)
.Save(output);
}
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[9, 9]);
Assert.Equal(mergedColor, sourcePixels[199, 149]); Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByRectangleOutline() public void ImageShouldBeOverlayedByRectangleOutline()
{ {
string path = CreateOutputDirectory("Drawing", "Polygons"); string path = this.CreateOutputDirectory("Drawing", "Polygons");
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Rectangle.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Rectangle.png"))
.BackgroundColor(Color.Blue) {
.DrawPolygon(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) image
.Save(output); .BackgroundColor(Color.Blue)
} .DrawPolygon(Color.HotPink, 10, new Rectangle(10, 10, 190, 140))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[8, 8]); Assert.Equal(Color.HotPink, sourcePixels[8, 8]);
Assert.Equal(Color.HotPink, sourcePixels[198, 10]); Assert.Equal(Color.HotPink, sourcePixels[198, 10]);
Assert.Equal(Color.HotPink, sourcePixels[10, 50]); Assert.Equal(Color.HotPink, sourcePixels[10, 50]);
Assert.Equal(Color.Blue, sourcePixels[50, 50]); Assert.Equal(Color.Blue, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
} }

32
tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs

@ -16,18 +16,19 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldRecolorYellowToHotPink() public void ImageShouldRecolorYellowToHotPink()
{ {
string path = CreateOutputDirectory("Drawing", "RecolorImage"); string path = this.CreateOutputDirectory("Drawing", "RecolorImage");
var brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); RecolorBrush brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f);
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Fill(brush) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
.Save(output); {
image.Fill(brush)
.Save(output);
}
} }
} }
} }
@ -35,19 +36,20 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldRecolorYellowToHotPinkInARectangle() public void ImageShouldRecolorYellowToHotPinkInARectangle()
{ {
string path = CreateOutputDirectory("Drawing", "RecolorImage"); string path = this.CreateOutputDirectory("Drawing", "RecolorImage");
var brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); RecolorBrush brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f);
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/Shaped_{file.FileName}"))
{ {
var imageHeight = image.Height; using (FileStream output = File.OpenWrite($"{path}/Shaped_{file.FileName}"))
image.Fill(brush, new Rectangle(0, imageHeight/2 - imageHeight/4, image.Width, imageHeight/2)) {
.Save(output); int imageHeight = image.Height;
image.Fill(brush, new Rectangle(0, imageHeight/2 - imageHeight/4, image.Width, imageHeight/2))
.Save(output);
}
} }
} }
} }

102
tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs

@ -5,11 +5,8 @@
namespace ImageSharp.Tests.Drawing namespace ImageSharp.Tests.Drawing
{ {
using Drawing;
using ImageSharp.Drawing;
using ImageSharp.Drawing.Shapes; using ImageSharp.Drawing.Shapes;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Numerics; using System.Numerics;
using Xunit; using Xunit;
@ -19,83 +16,84 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledPolygon() public void ImageShouldBeOverlayedByFilledPolygon()
{ {
string path = CreateOutputDirectory("Drawing", "FilledBezier"); string path = this.CreateOutputDirectory("Drawing", "FilledBezier");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 400), new Vector2(10, 400),
new Vector2(30, 10), new Vector2(30, 10),
new Vector2(240, 30), new Vector2(240, 30),
new Vector2(300, 400) new Vector2(300, 400)
}; };
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.Fill(Color.HotPink,new BezierPolygon(simplePath)) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(Color.HotPink,new BezierPolygon(simplePath))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
//top of curve //top of curve
Assert.Equal(Color.HotPink, sourcePixels[138, 116]); Assert.Equal(Color.HotPink, sourcePixels[138, 116]);
//start points //start points
Assert.Equal(Color.HotPink, sourcePixels[10, 400]); Assert.Equal(Color.HotPink, sourcePixels[10, 400]);
Assert.Equal(Color.HotPink, sourcePixels[300, 400]); Assert.Equal(Color.HotPink, sourcePixels[300, 400]);
//curve points should not be never be set //curve points should not be never be set
Assert.Equal(Color.Blue, sourcePixels[30, 10]); Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]); Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should not be empty // inside shape should not be empty
Assert.Equal(Color.HotPink, sourcePixels[200, 250]); Assert.Equal(Color.HotPink, sourcePixels[200, 250]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledPolygonOpacity() public void ImageShouldBeOverlayedByFilledPolygonOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "FilledBezier"); string path = this.CreateOutputDirectory("Drawing", "FilledBezier");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 400), new Vector2(10, 400),
new Vector2(30, 10), new Vector2(30, 10),
new Vector2(240, 30), new Vector2(240, 30),
new Vector2(300, 400) new Vector2(300, 400)
}; };
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.Fill(color, new BezierPolygon(simplePath)) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(color, new BezierPolygon(simplePath))
.Save(output);
}
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
//top of curve //top of curve
Assert.Equal(mergedColor, sourcePixels[138, 116]); Assert.Equal(mergedColor, sourcePixels[138, 116]);
//start points //start points
Assert.Equal(mergedColor, sourcePixels[10, 400]); Assert.Equal(mergedColor, sourcePixels[10, 400]);
Assert.Equal(mergedColor, sourcePixels[300, 400]); Assert.Equal(mergedColor, sourcePixels[300, 400]);
//curve points should not be never be set //curve points should not be never be set
Assert.Equal(Color.Blue, sourcePixels[30, 10]); Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]); Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should not be empty // inside shape should not be empty
Assert.Equal(mergedColor, sourcePixels[200, 250]); Assert.Equal(mergedColor, sourcePixels[200, 250]);
}
} }
} }
} }
} }

138
tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs

@ -5,12 +5,9 @@
namespace ImageSharp.Tests.Drawing namespace ImageSharp.Tests.Drawing
{ {
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using Xunit; using Xunit;
using Drawing;
using ImageSharp.Drawing;
using System.Numerics; using System.Numerics;
using ImageSharp.Drawing.Shapes; using ImageSharp.Drawing.Shapes;
@ -19,41 +16,42 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByPolygonOutline() public void ImageShouldBeOverlayedByPolygonOutline()
{ {
string path = CreateOutputDirectory("Drawing", "ComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(93, 85), new Vector2(93, 85),
new Vector2(65, 137)); new Vector2(65, 137));
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[11, 11]); Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 50]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
Assert.Equal(Color.HotPink, sourcePixels[35, 100]); Assert.Equal(Color.HotPink, sourcePixels[35, 100]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole //inside hole
Assert.Equal(Color.Blue, sourcePixels[57, 99]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
}
} }
} }
@ -61,87 +59,89 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOverlap() public void ImageShouldBeOverlayedPolygonOutlineWithOverlap()
{ {
string path = CreateOutputDirectory("Drawing", "ComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(130, 40), new Vector2(130, 40),
new Vector2(65, 137)); new Vector2(65, 137));
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png"))
.BackgroundColor(Color.Blue) {
.Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1))
.Save(output);
using (var sourcePixels = image.Lock()) }
{
Assert.Equal(Color.HotPink, sourcePixels[11, 11]); using (PixelAccessor<Color> sourcePixels = image.Lock())
{
Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 50]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
Assert.Equal(Color.HotPink, sourcePixels[35, 100]); Assert.Equal(Color.HotPink, sourcePixels[35, 100]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole //inside hole
Assert.Equal(Color.Blue, sourcePixels[57, 99]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "ComplexPolygon"); string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon");
var simplePath = new LinearPolygon( LinearPolygon simplePath = new LinearPolygon(
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300)); new Vector2(50, 300));
var hole1 = new LinearPolygon( LinearPolygon hole1 = new LinearPolygon(
new Vector2(37, 85), new Vector2(37, 85),
new Vector2(93, 85), new Vector2(93, 85),
new Vector2(65, 137)); new Vector2(65, 137));
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) using (Image image = new Image(500, 500))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.Fill(color, new ComplexPolygon(simplePath, hole1)) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(color, new ComplexPolygon(simplePath, hole1))
.Save(output);
}
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[11, 11]); Assert.Equal(mergedColor, sourcePixels[11, 11]);
Assert.Equal(mergedColor, sourcePixels[200, 150]); Assert.Equal(mergedColor, sourcePixels[200, 150]);
Assert.Equal(mergedColor, sourcePixels[50, 50]); Assert.Equal(mergedColor, sourcePixels[50, 50]);
Assert.Equal(mergedColor, sourcePixels[35, 100]); Assert.Equal(mergedColor, sourcePixels[35, 100]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
//inside hole //inside hole
Assert.Equal(Color.Blue, sourcePixels[57, 99]); Assert.Equal(Color.Blue, sourcePixels[57, 99]);
}
} }
} }
} }
} }

156
tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs

@ -5,10 +5,8 @@
namespace ImageSharp.Tests.Drawing namespace ImageSharp.Tests.Drawing
{ {
using Drawing;
using ImageSharp.Drawing; using ImageSharp.Drawing;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Numerics; using System.Numerics;
using Xunit; using Xunit;
@ -19,155 +17,153 @@ namespace ImageSharp.Tests.Drawing
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledPolygon() public void ImageShouldBeOverlayedByFilledPolygon()
{ {
string path = CreateOutputDirectory("Drawing", "FilledPolygons"); string path = this.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}; };
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Simple.png")) using (Image image = new Image(500, 500))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
.BackgroundColor(Color.Blue) {
.FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true)) image
.Save(output); .BackgroundColor(Color.Blue)
} .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[11, 11]); Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 50]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledPolygon_NoAntialias() public void ImageShouldBeOverlayedByFilledPolygonNoAntialias()
{ {
string path = CreateOutputDirectory("Drawing", "FilledPolygons"); string path = this.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}; };
var image = new Image(500, 500);
using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Simple_NoAntialias.png")) using (FileStream output = File.OpenWrite($"{path}/Simple_NoAntialias.png"))
{ {
image image
.BackgroundColor(Color.Blue) .BackgroundColor(Color.Blue)
.FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(false)) .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(false))
.Save(output); .Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[11, 11]); Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
Assert.Equal(Color.HotPink, sourcePixels[200, 150]); Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
Assert.Equal(Color.HotPink, sourcePixels[50, 50]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledPolygon_Image() public void ImageShouldBeOverlayedByFilledPolygonImage()
{ {
string path = CreateOutputDirectory("Drawing", "FilledPolygons"); string path = this.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}; };
var brush = new ImageBrush(TestFile.Create(TestImages.Bmp.Car).CreateImage()); using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateImage())
var image = new Image(500, 500); using (Image image = new Image(500, 500))
using (FileStream output = File.OpenWrite($"{path}/Image.png")) using (FileStream output = File.OpenWrite($"{path}/Image.png"))
{ {
ImageBrush brush = new ImageBrush(brushImage);
image image
.BackgroundColor(Color.Blue) .BackgroundColor(Color.Blue)
.FillPolygon(brush, simplePath) .FillPolygon(brush, simplePath)
.Save(output); .Save(output);
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledPolygonOpacity() public void ImageShouldBeOverlayedByFilledPolygonOpacity()
{ {
string path = CreateOutputDirectory("Drawing", "FilledPolygons"); string path = this.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new[] { Vector2[] simplePath = new[] {
new Vector2(10, 10), new Vector2(10, 10),
new Vector2(200, 150), new Vector2(200, 150),
new Vector2(50, 300) new Vector2(50, 300)
}; };
var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150);
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) using (Image image = new Image(500, 500))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Opacity.png"))
.BackgroundColor(Color.Blue) {
.FillPolygon(color, simplePath) image
.Save(output); .BackgroundColor(Color.Blue)
} .FillPolygon(color, simplePath)
.Save(output);
}
//shift background color towards forground color by the opacity amount //shift background color towards forground color by the opacity amount
var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f));
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(mergedColor, sourcePixels[11, 11]); Assert.Equal(mergedColor, sourcePixels[11, 11]);
Assert.Equal(mergedColor, sourcePixels[200, 150]); Assert.Equal(mergedColor, sourcePixels[200, 150]);
Assert.Equal(mergedColor, sourcePixels[50, 50]); Assert.Equal(mergedColor, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
[Fact] [Fact]
public void ImageShouldBeOverlayedByFilledRectangle() public void ImageShouldBeOverlayedByFilledRectangle()
{ {
string path = CreateOutputDirectory("Drawing", "FilledPolygons"); string path = this.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new[] { using (Image image = new Image(500, 500))
new Vector2(10, 10),
new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150)
};
var image = new Image(500, 500);
using (FileStream output = File.OpenWrite($"{path}/Rectangle.png"))
{ {
image using (FileStream output = File.OpenWrite($"{path}/Rectangle.png"))
.BackgroundColor(Color.Blue) {
.Fill(Color.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new Rectangle(10,10, 190, 140))) image
.Save(output); .BackgroundColor(Color.Blue)
} .Fill(Color.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new Rectangle(10, 10, 190, 140)))
.Save(output);
}
using (var sourcePixels = image.Lock()) using (PixelAccessor<Color> sourcePixels = image.Lock())
{ {
Assert.Equal(Color.HotPink, sourcePixels[11, 11]); Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
Assert.Equal(Color.HotPink, sourcePixels[198, 10]); Assert.Equal(Color.HotPink, sourcePixels[198, 10]);
Assert.Equal(Color.HotPink, sourcePixels[10, 50]); Assert.Equal(Color.HotPink, sourcePixels[10, 50]);
Assert.Equal(Color.HotPink, sourcePixels[50, 50]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
Assert.Equal(Color.Blue, sourcePixels[2, 2]); Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
} }
} }
} }

15
tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs

@ -9,8 +9,6 @@ namespace ImageSharp.Tests
{ {
using System.IO; using System.IO;
using Formats;
using Xunit; using Xunit;
public class BitmapTests : FileTestBase public class BitmapTests : FileTestBase
@ -23,19 +21,20 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("BitsPerPixel")] [MemberData(nameof(BitsPerPixel))]
public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel) public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel)
{ {
string path = CreateOutputDirectory("Bmp"); string path = this.CreateOutputDirectory("Bmp");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileNameWithoutExtension(bitsPerPixel); string filename = file.GetFileNameWithoutExtension(bitsPerPixel);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}.bmp"))
{ {
image.Save(output, new BmpEncoder { BitsPerPixel = bitsPerPixel }); using (FileStream output = File.OpenWrite($"{path}/{filename}.bmp"))
{
image.Save(output, new BmpEncoder { BitsPerPixel = bitsPerPixel });
}
} }
} }
} }

105
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -16,17 +16,18 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ResolutionShouldChange() public void ResolutionShouldChange()
{ {
string path = CreateOutputDirectory("Resolution"); string path = this.CreateOutputDirectory("Resolution");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.VerticalResolution = 150; using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
image.HorizontalResolution = 150; {
image.Save(output); image.VerticalResolution = 150;
image.HorizontalResolution = 150;
image.Save(output);
}
} }
} }
} }
@ -34,45 +35,31 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageCanEncodeToString() public void ImageCanEncodeToString()
{ {
string path = CreateOutputDirectory("ToString"); string path = this.CreateOutputDirectory("ToString");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
{
string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; string filename = path + "/" + file.FileNameWithoutExtension + ".txt";
File.WriteAllText(filename, image.ToBase64String()); File.WriteAllText(filename, image.ToBase64String());
}
} }
} }
[Fact] [Fact]
public void DecodeThenEncodeImageFromStreamShouldSucceed() public void DecodeThenEncodeImageFromStreamShouldSucceed()
{ {
string path = CreateOutputDirectory("Encode"); string path = this.CreateOutputDirectory("Encode");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
//Image<Bgr565> image = file.CreateImage().To<Bgr565>();
//Image<Bgra4444> image = file.CreateImage().To<Bgra4444>();
//Image<Bgra5551> image = file.CreateImage().To<Bgra5551>();
//Image<Byte4> image = file.CreateImage().To<Byte4>();
//Image<HalfSingle> image = file.CreateImage().To<HalfSingle>();
//Image<HalfVector2> image = file.CreateImage().To<HalfVector2>();
//Image<HalfVector4> image = file.CreateImage().To<HalfVector4>();
//Image<Rg32> image = file.CreateImage().To<Rg32>();
//Image<Rgba1010102> image = file.CreateImage().To<Rgba1010102>();
//Image<Rgba64> image = file.CreateImage().To<Rgba64>();
//Image<NormalizedByte2> image = file.CreateImage().To<NormalizedByte2>();
//Image<NormalizedByte4> image = file.CreateImage().To<NormalizedByte4>();
//Image<NormalizedShort2> image = file.CreateImage().To<NormalizedShort2>();
//Image<NormalizedShort4> image = file.CreateImage().To<NormalizedShort4>();
//Image<Short2> image = file.CreateImage().To<Short2>();
//Image<Short4> image = file.CreateImage().To<Short4>();
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Save(output); using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{
image.Save(output);
}
} }
} }
} }
@ -80,35 +67,39 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void QuantizeImageShouldPreserveMaximumColorPrecision() public void QuantizeImageShouldPreserveMaximumColorPrecision()
{ {
string path = CreateOutputDirectory("Quantize"); string path = this.CreateOutputDirectory("Quantize");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image srcImage = file.CreateImage())
// Copy the original pixels to save decoding time.
Color[] pixels = new Color[image.Width * image.Height];
Array.Copy(image.Pixels, pixels, image.Pixels.Length);
using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}"))
{ {
image.Quantize(Quantization.Octree) using (Image image = new Image(srcImage))
.Save(output, image.CurrentImageFormat); {
using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}"))
{
image.Quantize(Quantization.Octree)
.Save(output, image.CurrentImageFormat);
} }
}
image.SetPixels(image.Width, image.Height, pixels); using (Image image = new Image(srcImage))
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) {
{ using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
image.Quantize(Quantization.Wu) {
.Save(output, image.CurrentImageFormat); image.Quantize(Quantization.Wu)
} .Save(output, image.CurrentImageFormat);
}
}
image.SetPixels(image.Width, image.Height, pixels); using (Image image = new Image(srcImage))
using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) {
{ using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}"))
image.Quantize(Quantization.Palette) {
.Save(output, image.CurrentImageFormat); image.Quantize(Quantization.Palette)
.Save(output, image.CurrentImageFormat);
}
}
} }
} }
} }
@ -116,7 +107,7 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageCanConvertFormat() public void ImageCanConvertFormat()
{ {
string path = CreateOutputDirectory("Format"); string path = this.CreateOutputDirectory("Format");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
@ -147,7 +138,7 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldPreservePixelByteOrderWhenSerialized() public void ImageShouldPreservePixelByteOrderWhenSerialized()
{ {
string path = CreateOutputDirectory("Serialized"); string path = this.CreateOutputDirectory("Serialized");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {

16
tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs

@ -31,9 +31,11 @@ namespace ImageSharp.Tests
public void LoadBaselineImage<TColor>(TestImageProvider<TColor> provider) public void LoadBaselineImage<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
var image = provider.GetImage(); using (Image<TColor> image = provider.GetImage())
Assert.NotNull(image); {
provider.Utility.SaveTestOutputFile(image, "bmp"); Assert.NotNull(image);
provider.Utility.SaveTestOutputFile(image, "bmp");
}
} }
[Theory] // TODO: #18 [Theory] // TODO: #18
@ -41,9 +43,11 @@ namespace ImageSharp.Tests
public void LoadProgressiveImage<TColor>(TestImageProvider<TColor> provider) public void LoadProgressiveImage<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
var image = provider.GetImage(); using (Image<TColor> image = provider.GetImage())
Assert.NotNull(image); {
provider.Utility.SaveTestOutputFile(image, "bmp"); Assert.NotNull(image);
provider.Utility.SaveTestOutputFile(image, "bmp");
}
} }
} }
} }

61
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -29,19 +29,21 @@ namespace ImageSharp.Tests
public void OpenBaselineJpeg_SaveBmp<TColor>(TestImageProvider<TColor> provider) public void OpenBaselineJpeg_SaveBmp<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = provider.GetImage(); using (Image<TColor> image = provider.GetImage())
{
provider.Utility.SaveTestOutputFile(image, "bmp"); provider.Utility.SaveTestOutputFile(image, "bmp");
}
} }
[Theory] [Theory]
[WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)]
public void OpenProgressiveJpeg_SaveBmp<TColor>(TestImageProvider<TColor> provider) public void OpenProgressiveJpeg_SaveBmp<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = provider.GetImage(); using (Image<TColor> image = provider.GetImage())
{
provider.Utility.SaveTestOutputFile(image, "bmp"); provider.Utility.SaveTestOutputFile(image, "bmp");
}
} }
[Theory] [Theory]
@ -53,17 +55,19 @@ namespace ImageSharp.Tests
public void DecodeGenerated_SaveBmp<TColor>( public void DecodeGenerated_SaveBmp<TColor>(
TestImageProvider<TColor> provider, TestImageProvider<TColor> provider,
JpegSubsample subsample, JpegSubsample subsample,
int quality) int quality)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = provider.GetImage(); byte[] data;
using (Image<TColor> image = provider.GetImage())
JpegEncoder encoder = new JpegEncoder() { Subsample = subsample, Quality = quality };
byte[] data = new byte[65536];
using (MemoryStream ms = new MemoryStream(data))
{ {
image.Save(ms, encoder); JpegEncoder encoder = new JpegEncoder() { Subsample = subsample, Quality = quality };
data = new byte[65536];
using (MemoryStream ms = new MemoryStream(data))
{
image.Save(ms, encoder);
}
} }
// TODO: Automatic image comparers could help here a lot :P // TODO: Automatic image comparers could help here a lot :P
@ -75,23 +79,24 @@ namespace ImageSharp.Tests
[Theory] [Theory]
[WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.StandardImageClass)] [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.StandardImageClass)]
public void DecodeGenerated_MetadataOnly<TColor>( public void DecodeGenerated_MetadataOnly<TColor>(
TestImageProvider<TColor> provider) TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = provider.GetImage(); using (Image<TColor> image = provider.GetImage())
using (MemoryStream ms = new MemoryStream())
{ {
image.Save(ms, new JpegEncoder()); using (MemoryStream ms = new MemoryStream())
ms.Seek(0, SeekOrigin.Begin);
Image<TColor> mirror = provider.Factory.CreateImage(1, 1);
using (JpegDecoderCore decoder = new JpegDecoderCore())
{ {
decoder.Decode(mirror, ms, true); image.Save(ms, new JpegEncoder());
ms.Seek(0, SeekOrigin.Begin);
Assert.Equal(decoder.ImageWidth, image.Width);
Assert.Equal(decoder.ImageHeight, image.Height); Image<TColor> mirror = provider.Factory.CreateImage(1, 1);
using (JpegDecoderCore decoder = new JpegDecoderCore())
{
decoder.Decode(mirror, ms, true);
Assert.Equal(decoder.ImageWidth, image.Width);
Assert.Equal(decoder.ImageHeight, image.Height);
}
} }
} }
} }

44
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -34,19 +34,16 @@ namespace ImageSharp.Tests
public void LoadResizeSave<TColor>(TestImageProvider<TColor> provider, int quality, JpegSubsample subsample) public void LoadResizeSave<TColor>(TestImageProvider<TColor> provider, int quality, JpegSubsample subsample)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = provider.GetImage() using (Image<TColor> image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))
.Resize(new ResizeOptions {
{ image.Quality = quality;
Size = new Size(150, 100), image.ExifProfile = null; // Reduce the size of the file
Mode = ResizeMode.Max JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality };
});
image.Quality = quality;
image.ExifProfile = null; // Reduce the size of the file
JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality };
provider.Utility.TestName += $"{subsample}_Q{quality}"; provider.Utility.TestName += $"{subsample}_Q{quality}";
provider.Utility.SaveTestOutputFile(image, "png"); provider.Utility.SaveTestOutputFile(image, "png");
provider.Utility.SaveTestOutputFile(image, "jpg", encoder); provider.Utility.SaveTestOutputFile(image, "jpg", encoder);
}
} }
[Theory] [Theory]
@ -55,20 +52,21 @@ namespace ImageSharp.Tests
public void OpenBmp_SaveJpeg<TColor>(TestImageProvider<TColor> provider, JpegSubsample subSample, int quality) public void OpenBmp_SaveJpeg<TColor>(TestImageProvider<TColor> provider, JpegSubsample subSample, int quality)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = provider.GetImage(); using (Image<TColor> image = provider.GetImage())
ImagingTestCaseUtility utility = provider.Utility;
utility.TestName += "_" + subSample + "_Q" + quality;
using (var outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg")))
{ {
var encoder = new JpegEncoder() ImagingTestCaseUtility utility = provider.Utility;
utility.TestName += "_" + subSample + "_Q" + quality;
using (FileStream outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg")))
{ {
Subsample = subSample, JpegEncoder encoder = new JpegEncoder()
Quality = quality {
}; Subsample = subSample,
Quality = quality
};
image.Save(outputStream, encoder); image.Save(outputStream, encoder);
}
} }
} }
} }

85
tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs

@ -19,7 +19,6 @@ namespace ImageSharp.Tests
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = factory.CreateImage(10, 10); Image<TColor> image = factory.CreateImage(10, 10);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
@ -35,6 +34,7 @@ namespace ImageSharp.Tests
} }
} }
} }
return image; return image;
} }
@ -43,24 +43,21 @@ namespace ImageSharp.Tests
public void CopyStretchedRGBTo_FromOrigo<TColor>(TestImageProvider<TColor> provider) public void CopyStretchedRGBTo_FromOrigo<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> src = provider.GetImage(); using (Image<TColor> src = provider.GetImage())
using (Image<TColor> dest = provider.Factory.CreateImage(8, 8))
PixelArea<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz); using (PixelArea<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz))
Image<TColor> dest = provider.Factory.CreateImage(8, 8); using (PixelAccessor<TColor> s = src.Lock())
using (PixelAccessor<TColor> d = dest.Lock())
using (var s = src.Lock())
{ {
using (var d = dest.Lock()) s.CopyRGBBytesStretchedTo(area, 0, 0);
{ d.CopyFrom(area, 0, 0);
s.CopyRGBBytesStretchedTo(area, 0, 0);
d.CopyFrom(area, 0, 0);
Assert.Equal(s[0, 0], d[0, 0]); Assert.Equal(s[0, 0], d[0, 0]);
Assert.Equal(s[7, 0], d[7, 0]); Assert.Equal(s[7, 0], d[7, 0]);
Assert.Equal(s[0, 7], d[0, 7]); Assert.Equal(s[0, 7], d[0, 7]);
Assert.Equal(s[7, 7], d[7, 7]); Assert.Equal(s[7, 7], d[7, 7]);
}
} }
} }
[Theory] [Theory]
@ -68,45 +65,41 @@ namespace ImageSharp.Tests
public void CopyStretchedRGBTo_WithOffset<TColor>(TestImageProvider<TColor> provider) public void CopyStretchedRGBTo_WithOffset<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> src = provider.GetImage(); using (Image<TColor> src = provider.GetImage())
using (PixelArea<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz))
PixelArea<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz); using (Image<TColor> dest = provider.Factory.CreateImage(8, 8))
Image<TColor> dest = provider.Factory.CreateImage(8, 8);
using (PixelAccessor<TColor> s = src.Lock()) using (PixelAccessor<TColor> s = src.Lock())
using (PixelAccessor<TColor> d = dest.Lock())
{ {
using (var d = dest.Lock()) s.CopyRGBBytesStretchedTo(area, 7, 6);
{ d.CopyFrom(area, 0, 0);
s.CopyRGBBytesStretchedTo(area, 7, 6);
d.CopyFrom(area, 0, 0);
Assert.Equal(s[6, 7], d[0, 0]); Assert.Equal(s[6, 7], d[0, 0]);
Assert.Equal(s[6, 8], d[0, 1]); Assert.Equal(s[6, 8], d[0, 1]);
Assert.Equal(s[7, 8], d[1, 1]); Assert.Equal(s[7, 8], d[1, 1]);
Assert.Equal(s[6, 9], d[0, 2]); Assert.Equal(s[6, 9], d[0, 2]);
Assert.Equal(s[6, 9], d[0, 3]); Assert.Equal(s[6, 9], d[0, 3]);
Assert.Equal(s[6, 9], d[0, 7]); Assert.Equal(s[6, 9], d[0, 7]);
Assert.Equal(s[7, 9], d[1, 2]); Assert.Equal(s[7, 9], d[1, 2]);
Assert.Equal(s[7, 9], d[1, 3]); Assert.Equal(s[7, 9], d[1, 3]);
Assert.Equal(s[7, 9], d[1, 7]); Assert.Equal(s[7, 9], d[1, 7]);
Assert.Equal(s[9, 9], d[3, 2]); Assert.Equal(s[9, 9], d[3, 2]);
Assert.Equal(s[9, 9], d[3, 3]); Assert.Equal(s[9, 9], d[3, 3]);
Assert.Equal(s[9, 9], d[3, 7]); Assert.Equal(s[9, 9], d[3, 7]);
Assert.Equal(s[9, 7], d[3, 0]); Assert.Equal(s[9, 7], d[3, 0]);
Assert.Equal(s[9, 7], d[4, 0]); Assert.Equal(s[9, 7], d[4, 0]);
Assert.Equal(s[9, 7], d[7, 0]); Assert.Equal(s[9, 7], d[7, 0]);
Assert.Equal(s[9, 9], d[3, 2]); Assert.Equal(s[9, 9], d[3, 2]);
Assert.Equal(s[9, 9], d[4, 2]); Assert.Equal(s[9, 9], d[4, 2]);
Assert.Equal(s[9, 9], d[7, 2]); Assert.Equal(s[9, 9], d[7, 2]);
Assert.Equal(s[9, 9], d[4, 3]); Assert.Equal(s[9, 9], d[4, 3]);
Assert.Equal(s[9, 9], d[7, 7]); Assert.Equal(s[9, 9], d[7, 7]);
}
} }
} }
} }

4
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs

@ -6,8 +6,6 @@
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Formats.Jpg namespace ImageSharp.Tests.Formats.Jpg
{ {
using System.Numerics;
using ImageSharp.Formats;
using ImageSharp.Formats.Jpg; using ImageSharp.Formats.Jpg;
using Xunit; using Xunit;
@ -97,7 +95,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); Assert.Equal(expected, actual, new ApproximateFloatComparer(2f));
} }
} }
[Theory] [Theory]
[InlineData(42)] [InlineData(42)]
[InlineData(1)] [InlineData(1)]

20
tests/ImageSharp.Tests/Formats/Png/PngTests.cs

@ -23,12 +23,13 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png"))
{ {
image.Quality = 256; using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png"))
image.Save(output, new PngFormat()); {
image.Quality = 256;
image.Save(output, new PngFormat());
}
} }
} }
} }
@ -42,11 +43,12 @@ namespace ImageSharp.Tests
Files, Files,
file => file =>
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png"))
{ {
image.SaveAsPng(output); using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png"))
{
image.SaveAsPng(output);
}
} }
}); });
} }

9
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -23,10 +23,11 @@ namespace ImageSharp.Tests
}); });
TestFile file = TestFile.Create(TestImages.Bmp.Car); TestFile file = TestFile.Create(TestImages.Bmp.Car);
Image image = new Image(file.Bytes); using (Image image = new Image(file.Bytes))
{
Assert.Equal(600, image.Width); Assert.Equal(600, image.Width);
Assert.Equal(450, image.Height); Assert.Equal(450, image.Height);
}
} }
} }
} }

118
tests/ImageSharp.Tests/Image/PixelAccessorTests.cs

@ -19,8 +19,7 @@ namespace ImageSharp.Tests
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Image<TColor> image = factory.CreateImage(10, 10); Image<TColor> image = factory.CreateImage(10, 10);
using (PixelAccessor<TColor> pixels = image.Lock())
using (var pixels = image.Lock())
{ {
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
@ -47,31 +46,33 @@ namespace ImageSharp.Tests
public void CopyTo_Then_CopyFrom_OnFullImageRect<TColor>(TestImageProvider<TColor> provider, ComponentOrder order) public void CopyTo_Then_CopyFrom_OnFullImageRect<TColor>(TestImageProvider<TColor> provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
var src = provider.GetImage(); using (Image<TColor> src = provider.GetImage())
var dest = new Image<TColor>(src.Width, src.Height);
using (PixelArea<TColor> area = new PixelArea<TColor>(src.Width, src.Height, order))
{ {
using (var srcPixels = src.Lock()) using (Image<TColor> dest = new Image<TColor>(src.Width, src.Height))
{ {
srcPixels.CopyTo(area, 0, 0); using (PixelArea<TColor> area = new PixelArea<TColor>(src.Width, src.Height, order))
} {
using (PixelAccessor<TColor> srcPixels = src.Lock())
{
srcPixels.CopyTo(area, 0, 0);
}
using (PixelAccessor<TColor> destPixels = dest.Lock())
{
destPixels.CopyFrom(area, 0, 0);
}
}
using (var destPixels = dest.Lock()) Assert.True(src.IsEquivalentTo(dest, false));
{
destPixels.CopyFrom(area, 0, 0);
} }
} }
Assert.True(src.IsEquivalentTo(dest, false));
} }
// TODO: Need a processor in the library with this signature // TODO: Need a processor in the library with this signature
private static void Fill<TColor>(Image<TColor> image, Rectangle region, TColor color) private static void Fill<TColor>(Image<TColor> image, Rectangle region, TColor color)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
using (var pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
for (int y = region.Top; y < region.Bottom; y++) for (int y = region.Top; y < region.Bottom; y++)
{ {
@ -88,87 +89,114 @@ namespace ImageSharp.Tests
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)]
public void CopyTo_Then_CopyFrom_WithOffset<TColor>(TestImageProvider<TColor> provider, ComponentOrder order) public void CopyToThenCopyFromWithOffset<TColor>(TestImageProvider<TColor> provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
var srcImage = provider.GetImage(); using (Image<TColor> destImage = new Image<TColor>(8, 8))
var color = default(TColor);
color.PackFromBytes(255, 0, 0, 255);
Fill(srcImage, new Rectangle(4, 4, 8, 8), color);
var destImage = new Image<TColor>(8, 8);
using (var srcPixels = srcImage.Lock())
{ {
using (var area = new PixelArea<TColor>(8, 8, order)) TColor color;
using (Image<TColor> srcImage = provider.GetImage())
{ {
srcPixels.CopyTo(area, 4, 4); color = default(TColor);
color.PackFromBytes(255, 0, 0, 255);
using (var destPixels = destImage.Lock()) Fill(srcImage, new Rectangle(4, 4, 8, 8), color);
using (PixelAccessor<TColor> srcPixels = srcImage.Lock())
{ {
destPixels.CopyFrom(area, 0, 0); using (PixelArea<TColor> area = new PixelArea<TColor>(8, 8, order))
{
srcPixels.CopyTo(area, 4, 4);
using (PixelAccessor<TColor> destPixels = destImage.Lock())
{
destPixels.CopyFrom(area, 0, 0);
}
}
} }
} }
}
provider.Utility.SourceFileOrDescription = order.ToString();
provider.Utility.SaveTestOutputFile(destImage, "bmp");
var expectedImage = new Image<TColor>(8, 8).Fill(color); provider.Utility.SourceFileOrDescription = order.ToString();
provider.Utility.SaveTestOutputFile(destImage, "bmp");
Assert.True(destImage.IsEquivalentTo(expectedImage)); using (Image<TColor> expectedImage = new Image<TColor>(8, 8).Fill(color))
{
Assert.True(destImage.IsEquivalentTo(expectedImage));
}
}
} }
[Fact] [Fact]
public void CopyFromZYX() public void CopyFromZYX()
{ {
CopyFromZYX(new Image(1, 1)); using (Image<Color> image = new Image<Color>(1, 1))
{
CopyFromZYX(image);
}
} }
[Fact] [Fact]
public void CopyFromZYXOptimized() public void CopyFromZYXOptimized()
{ {
CopyFromZYX(new Image(1, 1)); using (Image image = new Image(1, 1))
{
CopyFromZYX(image);
}
} }
[Fact] [Fact]
public void CopyFromZYXW() public void CopyFromZYXW()
{ {
CopyFromZYXW(new Image(1, 1)); using (Image<Color> image = new Image<Color>(1, 1))
{
CopyFromZYXW(image);
}
} }
[Fact] [Fact]
public void CopyFromZYXWOptimized() public void CopyFromZYXWOptimized()
{ {
CopyFromZYXW(new Image(1, 1)); using (Image image = new Image(1, 1))
{
CopyFromZYXW(image);
}
} }
[Fact] [Fact]
public void CopyToZYX() public void CopyToZYX()
{ {
CopyToZYX(new Image(1, 1)); using (Image<Color> image = new Image<Color>(1, 1))
{
CopyToZYX(image);
}
} }
[Fact] [Fact]
public void CopyToZYXOptimized() public void CopyToZYXOptimized()
{ {
CopyToZYX(new Image(1, 1)); using (Image image = new Image(1, 1))
{
CopyToZYX(image);
}
} }
[Fact] [Fact]
public void CopyToZYXW() public void CopyToZYXW()
{ {
CopyToZYXW(new Image(1, 1)); using (Image<Color> image = new Image<Color>(1, 1))
{
CopyToZYXW(image);
}
} }
[Fact] [Fact]
public void CopyToZYXWOptimized() public void CopyToZYXWOptimized()
{ {
CopyToZYXW(new Image(1, 1)); using (Image image = new Image(1, 1))
{
CopyToZYXW(image);
}
} }
private static void CopyFromZYX<TColor>(Image<TColor> image) private static void CopyFromZYX<TColor>(Image<TColor> image)

74
tests/ImageSharp.Tests/Image/PixelPoolTests.cs

@ -0,0 +1,74 @@
// <copyright file="PixelPoolTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System.Linq;
using Xunit;
/// <summary>
/// Tests the <see cref="PixelAccessor"/> class.
/// </summary>
public class PixelPoolTests
{
[Fact]
public void PixelPoolRentsMinimumSize()
{
Color[] pixels = PixelPool<Color>.RentPixels(1024);
Assert.True(pixels.Length >= 1024);
}
[Fact]
public void PixelPoolRentsEmptyArray()
{
for (int i = 16; i < 1024; i += 16)
{
Color[] pixels = PixelPool<Color>.RentPixels(i);
Assert.True(pixels.All(p => p == default(Color)));
PixelPool<Color>.ReturnPixels(pixels);
}
for (int i = 16; i < 1024; i += 16)
{
Color[] pixels = PixelPool<Color>.RentPixels(i);
Assert.True(pixels.All(p => p == default(Color)));
PixelPool<Color>.ReturnPixels(pixels);
}
}
[Fact]
public void PixelPoolDoesNotThrowWhenReturningNonPooled()
{
Color[] pixels = new Color[1024];
PixelPool<Color>.ReturnPixels(pixels);
Assert.True(pixels.Length >= 1024);
}
[Fact]
public void PixelPoolCleansRentedArray()
{
Color[] pixels = PixelPool<Color>.RentPixels(256);
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = Color.Azure;
}
Assert.True(pixels.All(p => p == Color.Azure));
PixelPool<Color>.ReturnPixels(pixels);
Assert.True(pixels.All(p => p == default(Color)));
}
}
}

20
tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs

@ -19,39 +19,35 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("AlphaValues")] [MemberData(nameof(AlphaValues))]
public void ImageShouldApplyAlphaFilter(int value) public void ImageShouldApplyAlphaFilter(int value)
{ {
string path = CreateOutputDirectory("Alpha"); string path = this.CreateOutputDirectory("Alpha");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Alpha(value) image.Alpha(value).Save(output);
.Save(output);
} }
} }
} }
[Theory] [Theory]
[MemberData("AlphaValues")] [MemberData(nameof(AlphaValues))]
public void ImageShouldApplyAlphaFilterInBox(int value) public void ImageShouldApplyAlphaFilterInBox(int value)
{ {
string path = CreateOutputDirectory("Alpha"); string path = this.CreateOutputDirectory("Alpha");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Alpha(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)) image.Alpha(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output);
.Save(output);
} }
} }
} }

19
tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs

@ -26,25 +26,22 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("OrientationValues")] [MemberData(nameof(OrientationValues))]
public void ImageShouldFlip(RotateType rotateType, FlipType flipType, ushort orientation) public void ImageShouldFlip(RotateType rotateType, FlipType flipType, ushort orientation)
{ {
string path = CreateOutputDirectory("AutoOrient"); string path = this.CreateOutputDirectory("AutoOrient");
TestFile file = TestFile.Create(TestImages.Bmp.F); TestFile file = TestFile.Create(TestImages.Bmp.F);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
image.ExifProfile = new ExifProfile();
image.ExifProfile.SetValue(ExifTag.Orientation, orientation);
using (FileStream before = File.OpenWrite($"{path}/before-{file.FileName}"))
{ {
image.ExifProfile = new ExifProfile();
image.ExifProfile.SetValue(ExifTag.Orientation, orientation);
using (FileStream before = File.OpenWrite($"{path}/before-{file.FileName}"))
using (FileStream after = File.OpenWrite($"{path}/after-{file.FileName}")) using (FileStream after = File.OpenWrite($"{path}/after-{file.FileName}"))
{ {
image.RotateFlip(rotateType, flipType) image.RotateFlip(rotateType, flipType).Save(before).AutoOrient().Save(after);
.Save(before)
.AutoOrient()
.Save(after);
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyBackgroundColorFilter() public void ImageShouldApplyBackgroundColorFilter()
{ {
string path = CreateOutputDirectory("BackgroundColor"); string path = this.CreateOutputDirectory("BackgroundColor");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.BackgroundColor(Color.HotPink) image.BackgroundColor(Color.HotPink).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("BinaryThresholdValues")] [MemberData(nameof(BinaryThresholdValues))]
public void ImageShouldApplyBinaryThresholdFilter(float value) public void ImageShouldApplyBinaryThresholdFilter(float value)
{ {
string path = CreateOutputDirectory("BinaryThreshold"); string path = this.CreateOutputDirectory("BinaryThreshold");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.BinaryThreshold(value) image.BinaryThreshold(value).Save(output);
.Save(output);
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyBlackWhiteFilter() public void ImageShouldApplyBlackWhiteFilter()
{ {
string path = CreateOutputDirectory("BlackWhite"); string path = this.CreateOutputDirectory("BlackWhite");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.BlackWhite() image.BlackWhite().Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("BoxBlurValues")] [MemberData(nameof(BoxBlurValues))]
public void ImageShouldApplyBoxBlurFilter(int value) public void ImageShouldApplyBoxBlurFilter(int value)
{ {
string path = CreateOutputDirectory("BoxBlur"); string path = this.CreateOutputDirectory("BoxBlur");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.BoxBlur(value) image.BoxBlur(value).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("BrightnessValues")] [MemberData(nameof(BrightnessValues))]
public void ImageShouldApplyBrightnessFilter(int value) public void ImageShouldApplyBrightnessFilter(int value)
{ {
string path = CreateOutputDirectory("Brightness"); string path = this.CreateOutputDirectory("Brightness");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Brightness(value) image.Brightness(value).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs

@ -26,20 +26,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("ColorBlindnessFilters")] [MemberData(nameof(ColorBlindnessFilters))]
public void ImageShouldApplyColorBlindnessFilter(ColorBlindness colorBlindness) public void ImageShouldApplyColorBlindnessFilter(ColorBlindness colorBlindness)
{ {
string path = CreateOutputDirectory("ColorBlindness"); string path = this.CreateOutputDirectory("ColorBlindness");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(colorBlindness); string filename = file.GetFileName(colorBlindness);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.ColorBlindness(colorBlindness) image.ColorBlindness(colorBlindness).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs

@ -19,19 +19,17 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("ContrastValues")] [MemberData(nameof(ContrastValues))]
public void ImageShouldApplyContrastFilter(int value) public void ImageShouldApplyContrastFilter(int value)
{ {
string path = CreateOutputDirectory("Contrast"); string path = this.CreateOutputDirectory("Contrast");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Contrast(value) image.Contrast(value).Save(output);
.Save(output);
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/CropTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyCropSampler() public void ImageShouldApplyCropSampler()
{ {
string path = CreateOutputDirectory("Crop"); string path = this.CreateOutputDirectory("Crop");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Crop(image.Width / 2, image.Height / 2) image.Crop(image.Width / 2, image.Height / 2).Save(output);
.Save(output);
} }
} }
} }

15
tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs

@ -31,32 +31,29 @@ namespace ImageSharp.Tests
[MemberData(nameof(DetectEdgesFilters))] [MemberData(nameof(DetectEdgesFilters))]
public void ImageShouldApplyDetectEdgesFilter(EdgeDetection detector) public void ImageShouldApplyDetectEdgesFilter(EdgeDetection detector)
{ {
string path = CreateOutputDirectory("DetectEdges"); string path = this.CreateOutputDirectory("DetectEdges");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(detector); string filename = file.GetFileName(detector);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.DetectEdges(detector) image.DetectEdges(detector).Save(output);
.Save(output);
} }
} }
} }
[Theory] [Theory]
[MemberData("DetectEdgesFilters")] [MemberData(nameof(DetectEdgesFilters))]
public void ImageShouldApplyDetectEdgesFilterInBox(EdgeDetection detector) public void ImageShouldApplyDetectEdgesFilterInBox(EdgeDetection detector)
{ {
string path = CreateOutputDirectory("DetectEdges"); string path = this.CreateOutputDirectory("DetectEdges");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(detector + "-InBox"); string filename = file.GetFileName(detector + "-InBox");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.DetectEdges(detector, new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) image.DetectEdges(detector, new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2))

10
tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("EntropyCropValues")] [MemberData(nameof(EntropyCropValues))]
public void ImageShouldApplyEntropyCropSampler(float value) public void ImageShouldApplyEntropyCropSampler(float value)
{ {
string path = CreateOutputDirectory("EntropyCrop"); string path = this.CreateOutputDirectory("EntropyCrop");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.EntropyCrop(value) image.EntropyCrop(value).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs

@ -20,20 +20,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("FlipValues")] [MemberData(nameof(FlipValues))]
public void ImageShouldFlip(FlipType flipType) public void ImageShouldFlip(FlipType flipType)
{ {
string path = CreateOutputDirectory("Flip"); string path = this.CreateOutputDirectory("Flip");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(flipType); string filename = file.GetFileName(flipType);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Flip(flipType) image.Flip(flipType).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("GaussianBlurValues")] [MemberData(nameof(GaussianBlurValues))]
public void ImageShouldApplyGaussianBlurFilter(int value) public void ImageShouldApplyGaussianBlurFilter(int value)
{ {
string path = CreateOutputDirectory("GaussianBlur"); string path = this.CreateOutputDirectory("GaussianBlur");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.GaussianBlur(value) image.GaussianBlur(value).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("GaussianSharpenValues")] [MemberData(nameof(GaussianSharpenValues))]
public void ImageShouldApplyGaussianSharpenFilter(int value) public void ImageShouldApplyGaussianSharpenFilter(int value)
{ {
string path = CreateOutputDirectory("GaussianSharpen"); string path = this.CreateOutputDirectory("GaussianSharpen");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.GaussianSharpen(value) image.GaussianSharpen(value).Save(output);
.Save(output);
} }
} }
} }

31
tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyGlowFilter() public void ImageShouldApplyGlowFilter()
{ {
string path = CreateOutputDirectory("Glow"); string path = this.CreateOutputDirectory("Glow");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Glow() image.Glow().Save(output);
.Save(output);
} }
} }
} }
@ -31,17 +29,15 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyGlowFilterColor() public void ImageShouldApplyGlowFilterColor()
{ {
string path = CreateOutputDirectory("Glow"); string path = this.CreateOutputDirectory("Glow");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName("Color"); string filename = file.GetFileName("Color");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Glow(Color.HotPink) image.Glow(Color.HotPink).Save(output);
.Save(output);
} }
} }
} }
@ -49,17 +45,15 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyGlowFilterRadius() public void ImageShouldApplyGlowFilterRadius()
{ {
string path = CreateOutputDirectory("Glow"); string path = this.CreateOutputDirectory("Glow");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName("Radius"); string filename = file.GetFileName("Radius");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Glow(image.Width / 4) image.Glow(image.Width / 4F).Save(output);
.Save(output);
} }
} }
} }
@ -67,17 +61,16 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyGlowFilterInBox() public void ImageShouldApplyGlowFilterInBox()
{ {
string path = CreateOutputDirectory("Glow"); string path = this.CreateOutputDirectory("Glow");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName("InBox"); string filename = file.GetFileName("InBox");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Glow(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) image.Glow(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2))
.Save(output); .Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs

@ -20,20 +20,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("GrayscaleValues")] [MemberData(nameof(GrayscaleValues))]
public void ImageShouldApplyGrayscaleFilter(GrayscaleMode value) public void ImageShouldApplyGrayscaleFilter(GrayscaleMode value)
{ {
string path = CreateOutputDirectory("Grayscale"); string path = this.CreateOutputDirectory("Grayscale");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Grayscale(value) image.Grayscale(value).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/HueTest.cs

@ -19,20 +19,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("HueValues")] [MemberData(nameof(HueValues))]
public void ImageShouldApplyHueFilter(int value) public void ImageShouldApplyHueFilter(int value)
{ {
string path = CreateOutputDirectory("Hue"); string path = this.CreateOutputDirectory("Hue");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Hue(value) image.Hue(value).Save(output);
.Save(output);
} }
} }
} }

16
tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs

@ -14,15 +14,13 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyInvertFilter() public void ImageShouldApplyInvertFilter()
{ {
string path = CreateOutputDirectory("Invert"); string path = this.CreateOutputDirectory("Invert");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Invert() image.Invert().Save(output);
.Save(output);
} }
} }
} }
@ -30,17 +28,15 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyInvertFilterInBox() public void ImageShouldApplyInvertFilterInBox()
{ {
string path = CreateOutputDirectory("Invert"); string path = this.CreateOutputDirectory("Invert");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName("InBox"); string filename = file.GetFileName("InBox");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Invert(new Rectangle(10, 10, image.Width / 2, image.Height / 2)) image.Invert(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output);
.Save(output);
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyKodachromeFilter() public void ImageShouldApplyKodachromeFilter()
{ {
string path = CreateOutputDirectory("Kodachrome"); string path = this.CreateOutputDirectory("Kodachrome");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Kodachrome() image.Kodachrome().Save(output);
.Save(output);
} }
} }
} }

15
tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyLomographFilter() public void ImageShouldApplyLomographFilter()
{ {
string path = CreateOutputDirectory("Lomograph"); string path = this.CreateOutputDirectory("Lomograph");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Lomograph() image.Lomograph().Save(output);
.Save(output);
} }
} }
} }
@ -31,17 +29,16 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyLomographFilterInBox() public void ImageShouldApplyLomographFilterInBox()
{ {
string path = CreateOutputDirectory("Lomograph"); string path = this.CreateOutputDirectory("Lomograph");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName("InBox"); string filename = file.GetFileName("InBox");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Lomograph(new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)) image.Lomograph(new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2))
.Save(output); .Save(output);
} }
} }
} }

21
tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs

@ -23,19 +23,17 @@ namespace ImageSharp.Tests
[MemberData(nameof(OilPaintValues))] [MemberData(nameof(OilPaintValues))]
public void ImageShouldApplyOilPaintFilter(Tuple<int, int> value) public void ImageShouldApplyOilPaintFilter(Tuple<int, int> value)
{ {
string path = CreateOutputDirectory("OilPaint"); string path = this.CreateOutputDirectory("OilPaint");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value); string filename = file.GetFileName(value);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
if (image.Width > value.Item2 && image.Height > value.Item2) if (image.Width > value.Item2 && image.Height > value.Item2)
{ {
image.OilPaint(value.Item1, value.Item2) image.OilPaint(value.Item1, value.Item2).Save(output);
.Save(output);
} }
} }
} }
@ -45,18 +43,19 @@ namespace ImageSharp.Tests
[MemberData(nameof(OilPaintValues))] [MemberData(nameof(OilPaintValues))]
public void ImageShouldApplyOilPaintFilterInBox(Tuple<int, int> value) public void ImageShouldApplyOilPaintFilterInBox(Tuple<int, int> value)
{ {
string path = CreateOutputDirectory("OilPaint"); string path = this.CreateOutputDirectory("OilPaint");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value + "-InBox"); string filename = file.GetFileName(value + "-InBox");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
if (image.Width > value.Item2 && image.Height > value.Item2)
{ {
using (FileStream output = File.OpenWrite($"{path}/{filename}")) if (image.Width > value.Item2 && image.Height > value.Item2)
{ {
image.OilPaint(value.Item1, value.Item2, new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)).Save(output); using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{
image.OilPaint(value.Item1, value.Item2, new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)).Save(output);
}
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/PadTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyPadSampler() public void ImageShouldApplyPadSampler()
{ {
string path = CreateOutputDirectory("Pad"); string path = this.CreateOutputDirectory("Pad");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Pad(image.Width + 50, image.Height + 50) image.Pad(image.Width + 50, image.Height + 50).Save(output);
.Save(output);
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs

@ -41,17 +41,15 @@ namespace ImageSharp.Tests
[MemberData(nameof(PixelateValues))] [MemberData(nameof(PixelateValues))]
public void ImageShouldApplyPixelateFilterInBox(int value) public void ImageShouldApplyPixelateFilterInBox(int value)
{ {
string path = CreateOutputDirectory("Pixelate"); string path = this.CreateOutputDirectory("Pixelate");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(value + "-InBox"); string filename = file.GetFileName(value + "-InBox");
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Pixelate(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)) image.Pixelate(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output);
.Save(output);
} }
} }
} }

8
tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs

@ -14,16 +14,14 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void ImageShouldApplyPolaroidFilter() public void ImageShouldApplyPolaroidFilter()
{ {
string path = CreateOutputDirectory("Polaroid"); string path = this.CreateOutputDirectory("Polaroid");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.Polaroid() image.Polaroid().Save(output);
.Save(output);
} }
} }
} }

76
tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs

@ -42,12 +42,10 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Resize(image.Width / 2, image.Height / 2, sampler, true) image.Resize(image.Width / 2, image.Height / 2, sampler, true).Save(output);
.Save(output);
} }
} }
} }
@ -63,12 +61,10 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Resize(image.Width / 3, 0, sampler, false) image.Resize(image.Width / 3, 0, sampler, false).Save(output);
.Save(output);
} }
} }
} }
@ -84,12 +80,10 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.Resize(0, image.Height / 3, sampler, false) image.Resize(0, image.Height / 3, sampler, false).Save(output);
.Save(output);
} }
} }
} }
@ -105,18 +99,16 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Sampler = sampler, Sampler = sampler,
Size = new Size(image.Width / 2, image.Height) Size = new Size(image.Width / 2, image.Height)
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }
@ -132,18 +124,16 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Sampler = sampler, Sampler = sampler,
Size = new Size(image.Width, image.Height / 2) Size = new Size(image.Width, image.Height / 2)
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }
@ -159,18 +149,16 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Size = new Size(image.Width + 200, image.Height), Size = new Size(image.Width + 200, image.Height),
Mode = ResizeMode.Pad Mode = ResizeMode.Pad
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }
@ -186,19 +174,17 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Sampler = sampler, Sampler = sampler,
Size = new Size(image.Width + 200, image.Height + 200), Size = new Size(image.Width + 200, image.Height + 200),
Mode = ResizeMode.BoxPad Mode = ResizeMode.BoxPad
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }
@ -214,19 +200,17 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Sampler = sampler, Sampler = sampler,
Size = new Size(300, 300), Size = new Size(300, 300),
Mode = ResizeMode.Max Mode = ResizeMode.Max
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }
@ -242,19 +226,17 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Sampler = sampler, Sampler = sampler,
Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * 95F)), Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)),
Mode = ResizeMode.Min Mode = ResizeMode.Min
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }
@ -270,19 +252,17 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(name); string filename = file.GetFileName(name);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions
{ {
Sampler = sampler, Sampler = sampler,
Size = new Size(image.Width / 2, image.Height), Size = new Size(image.Width / 2, image.Height),
Mode = ResizeMode.Stretch Mode = ResizeMode.Stretch
}; };
image.Resize(options) image.Resize(options).Save(output);
.Save(output);
} }
} }
} }

10
tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs

@ -22,20 +22,18 @@ namespace ImageSharp.Tests
}; };
[Theory] [Theory]
[MemberData("RotateFlipValues")] [MemberData(nameof(RotateFlipValues))]
public void ImageShouldRotateFlip(RotateType rotateType, FlipType flipType) public void ImageShouldRotateFlip(RotateType rotateType, FlipType flipType)
{ {
string path = CreateOutputDirectory("RotateFlip"); string path = this.CreateOutputDirectory("RotateFlip");
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
string filename = file.GetFileName(rotateType + "-" + flipType); string filename = file.GetFileName(rotateType + "-" + flipType);
Image image = file.CreateImage(); using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}")) using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{ {
image.RotateFlip(rotateType, flipType) image.RotateFlip(rotateType, flipType).Save(output);
.Save(output);
} }
} }
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save