Browse Source

Merge remote-tracking branch 'refs/remotes/origin/master' into feature/icc

pull/181/head
James Jackson-South 9 years ago
parent
commit
475b2340ca
  1. 5
      ImageSharp.sln
  2. 4
      README.md
  3. 2
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  4. 38
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  5. 9
      src/ImageSharp.Drawing/Text/DrawText.cs
  6. 7
      src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs
  7. 6
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  8. 27
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  9. 12
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  10. 50
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  11. 5
      src/ImageSharp/Formats/IImageDecoder.cs
  12. 7
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  13. 115
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  14. 12
      src/ImageSharp/Formats/Png/PngDecoder.cs
  15. 47
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  16. 66
      src/ImageSharp/Image.Create.cs
  17. 75
      src/ImageSharp/Image.Decode.cs
  18. 212
      src/ImageSharp/Image.FromBytes.cs
  19. 214
      src/ImageSharp/Image.FromFile.cs
  20. 246
      src/ImageSharp/Image.FromStream.cs
  21. 199
      src/ImageSharp/Image.cs
  22. 10
      src/ImageSharp/Image/IImageBase.cs
  23. 10
      src/ImageSharp/Image/IImageBase{TColor}.cs
  24. 41
      src/ImageSharp/Image/ImageBase{TColor}.cs
  25. 328
      src/ImageSharp/Image/Image{TColor}.cs
  26. 2
      src/ImageSharp/ImageSharp.csproj
  27. 9
      src/ImageSharp/MetaData/ImageMetaData.cs
  28. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  29. 7
      src/README.md
  30. 2
      tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs
  31. 2
      tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs
  32. 2
      tests/ImageSharp.Benchmarks/Image/DecodeGif.cs
  33. 2
      tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs
  34. 2
      tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs
  35. 2
      tests/ImageSharp.Benchmarks/Image/DecodePng.cs
  36. 2
      tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
  37. 2
      tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
  38. 2
      tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs
  39. 2
      tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs
  40. 2
      tests/ImageSharp.Benchmarks/Image/EncodePng.cs
  41. 2
      tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs
  42. 2
      tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs
  43. 27
      tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
  44. 2
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  45. 6
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  46. 7
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  47. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  48. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs
  49. 520
      tests/ImageSharp.Tests/Image/ImageLoadTests.cs
  50. 10
      tests/ImageSharp.Tests/Image/ImageTests.cs
  51. 6
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
  52. 4
      tests/ImageSharp.Tests/TestFile.cs
  53. 71
      tests/ImageSharp.Tests/TestFileSystem.cs
  54. 185
      tests/ImageSharp.Tests/TestFormat.cs
  55. 2
      tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs
  56. 2
      tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs

5
ImageSharp.sln

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
@ -22,6 +22,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"
ProjectSection(SolutionItems) = preProject
src\README.md = src\README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}"
EndProject

4
README.md

@ -74,7 +74,7 @@ Here's an example of the code required to resize an image using the default Bicu
On platforms supporting netstandard 1.3+
```csharp
using (Image image = new Image("foo.jpg"))
using (Image image = Image.Load("foo.jpg"))
{
image.Resize(image.Width / 2, image.Height / 2)
.Grayscale()
@ -85,7 +85,7 @@ on netstandard 1.1 - 1.2
```csharp
using (FileStream stream = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
using (Image image = new Image(stream))
using (Image image = Image.Load(stream))
{
image.Resize(image.Width / 2, image.Height / 2)
.Grayscale()

2
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<AssemblyTitle>ImageSharp.Drawing</AssemblyTitle>
<VersionPrefix>1.0.0-alpha4</VersionPrefix>
<VersionPrefix>1.0.0-alpha5</VersionPrefix>
<Authors>James Jackson-South and contributors</Authors>
<TargetFramework>netstandard1.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

38
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -65,6 +65,15 @@ namespace ImageSharp.Drawing.Processors
int maxX = Math.Min(source.Width, rect.Right);
int minY = Math.Max(0, rect.Top);
int maxY = Math.Min(source.Height, rect.Bottom);
if (minX >= maxX)
{
return; // no effect inside image;
}
if (minY >= maxY)
{
return; // no effect inside image;
}
ArrayPool<float> arrayPool = ArrayPool<float>.Shared;
@ -122,22 +131,33 @@ namespace ImageSharp.Drawing.Processors
int startX = (int)Math.Floor(scanStart);
int endX = (int)Math.Floor(scanEnd);
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
if (startX >= 0 && startX < scanline.Length)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
for (float x = endX; x < scanEnd; x += subpixelFraction)
if (endX >= 0 && endX < scanline.Length)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
for (int x = startX + 1; x < endX; x++)
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
if (nextX >= 0)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
}

9
src/ImageSharp.Drawing/Text/DrawText.cs

@ -18,6 +18,8 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
private static readonly Vector2 DefaultTextDpi = new Vector2(72);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
@ -169,7 +171,12 @@ namespace ImageSharp
TextRenderer renderer = new TextRenderer(glyphBuilder);
Vector2 dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
Vector2 dpi = DefaultTextDpi;
if (options.UseImageResolution)
{
dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
}
FontSpan style = new FontSpan(font)
{
ApplyKerning = options.ApplyKerning,

7
src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs

@ -35,6 +35,12 @@ namespace ImageSharp.Drawing
/// </summary>
public float TabWidth;
/// <summary>
/// Flag weather to use the current image resultion to for point size scaling.
/// If this is [false] the text renders at 72dpi otherwise it renders at Image resolution
/// </summary>
public bool UseImageResolution;
/// <summary>
/// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct.
/// </summary>
@ -45,6 +51,7 @@ namespace ImageSharp.Drawing
this.ApplyKerning = true;
this.TabWidth = 4;
this.AntialiasSubpixelDepth = 16;
this.UseImageResolution = false;
}
/// <summary>

6
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -26,13 +26,13 @@ namespace ImageSharp.Formats
public class BmpDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
new BmpDecoderCore().Decode(image, stream);
return new BmpDecoderCore(configuration).Decode<TColor>(stream);
}
}
}

27
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -43,21 +43,29 @@ namespace ImageSharp.Formats
/// </summary>
private BmpInfoHeader infoHeader;
private Configuration configuration;
/// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public BmpDecoderCore(Configuration configuration)
{
this.configuration = configuration;
}
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image, where the data should be set to.
/// Cannot be null (Nothing in Visual Basic).</param>
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="image"/> is null.</para>
/// <para>- or -</para>
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
public void Decode<TColor>(Image<TColor> image, Stream stream)
/// <returns>The decoded image.</returns>
public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
this.currentStream = stream;
@ -110,15 +118,14 @@ namespace ImageSharp.Formats
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > image.MaxWidth || this.infoHeader.Height > image.MaxHeight)
if (this.infoHeader.Width > Image<TColor>.MaxWidth || this.infoHeader.Height > Image<TColor>.MaxHeight)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
+ $"bigger then the max allowed size '{Image<TColor>.MaxWidth}x{Image<TColor>.MaxHeight}'");
}
image.InitPixels(this.infoHeader.Width, this.infoHeader.Height);
Image<TColor> image = Image.Create<TColor>(this.infoHeader.Width, this.infoHeader.Height, this.configuration);
using (PixelAccessor<TColor> pixels = image.Lock())
{
switch (this.infoHeader.Compression)
@ -151,6 +158,8 @@ namespace ImageSharp.Formats
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
}
return image;
}
catch (IndexOutOfRangeException e)
{

12
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -14,25 +14,27 @@ namespace ImageSharp.Formats
public class GifDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options);
this.Decode(image, stream, gifOptions);
return this.Decode<TColor>(configuration, stream, gifOptions);
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</param>
public void Decode<TColor>(Image<TColor> image, Stream stream, IGifDecoderOptions options)
/// <returns>The image thats been decoded.</returns>
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IGifDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
new GifDecoderCore<TColor>(options).Decode(image, stream);
return new GifDecoderCore<TColor>(options, configuration).Decode(stream);
}
}
}

50
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -28,9 +28,9 @@ namespace ImageSharp.Formats
private readonly IGifDecoderOptions options;
/// <summary>
/// The image to decode the information to.
/// The global configuration.
/// </summary>
private Image<TColor> decodedImage;
private readonly Configuration configuration;
/// <summary>
/// The currently loaded stream.
@ -67,25 +67,37 @@ namespace ImageSharp.Formats
/// </summary>
private GifGraphicsControlExtension graphicsControlExtension;
/// <summary>
/// The metadata
/// </summary>
private ImageMetaData metaData;
/// <summary>
/// The image to decode the information to.
/// </summary>
private Image<TColor> image;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TColor}"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public GifDecoderCore(IGifDecoderOptions options)
/// <param name="configuration">The configuration.</param>
public GifDecoderCore(IGifDecoderOptions options, Configuration configuration)
{
this.options = options ?? new GifDecoderOptions();
this.configuration = configuration ?? Configuration.Default;
}
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param>
public void Decode(Image<TColor> image, Stream stream)
/// <returns>The decoded image</returns>
public Image<TColor> Decode(Stream stream)
{
try
{
this.decodedImage = image;
this.metaData = new ImageMetaData();
this.currentStream = stream;
@ -144,6 +156,8 @@ namespace ImageSharp.Formats
ArrayPool<byte>.Shared.Return(this.globalColorTable);
}
}
return this.image;
}
/// <summary>
@ -212,11 +226,13 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
}
if (this.logicalScreenDescriptor.Width > this.decodedImage.MaxWidth || this.logicalScreenDescriptor.Height > this.decodedImage.MaxHeight)
/* // No point doing this as the max width/height is always int.Max and that always bigger than the max size of a gif which is stored in a short.
if (this.logicalScreenDescriptor.Width > Image<TColor>.MaxWidth || this.logicalScreenDescriptor.Height > Image<TColor>.MaxHeight)
{
throw new ArgumentOutOfRangeException(
$"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{this.decodedImage.MaxWidth}x{this.decodedImage.MaxHeight}'");
$"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image<TColor>.MaxWidth}x{Image<TColor>.MaxHeight}'");
}
*/
}
/// <summary>
@ -261,7 +277,7 @@ namespace ImageSharp.Formats
{
this.currentStream.Read(commentsBuffer, 0, length);
string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length);
this.decodedImage.MetaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
finally
{
@ -343,14 +359,14 @@ namespace ImageSharp.Formats
if (this.previousFrame == null)
{
this.decodedImage.MetaData.Quality = colorTableLength / 3;
this.metaData.Quality = colorTableLength / 3;
// This initializes the image to become fully transparent because the alpha channel is zero.
this.decodedImage.InitPixels(imageWidth, imageHeight);
this.image = Image.Create<TColor>(imageWidth, imageHeight, this.metaData, this.configuration);
this.SetFrameDelay(this.decodedImage.MetaData);
this.SetFrameDelay(this.metaData);
image = this.decodedImage;
image = this.image;
}
else
{
@ -368,7 +384,7 @@ namespace ImageSharp.Formats
this.RestoreToBackground(image);
this.decodedImage.Frames.Add(currentFrame);
this.image.Frames.Add(currentFrame);
}
int i = 0;
@ -441,7 +457,7 @@ namespace ImageSharp.Formats
return;
}
this.previousFrame = currentFrame == null ? this.decodedImage.ToFrame() : currentFrame;
this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -462,8 +478,8 @@ namespace ImageSharp.Formats
}
// Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.decodedImage.Width &&
this.restoreArea.Value.Height == this.decodedImage.Height)
if (this.restoreArea.Value.Width == this.image.Width &&
this.restoreArea.Value.Height == this.image.Height)
{
using (PixelAccessor<TColor> pixelAccessor = frame.Lock())
{

5
src/ImageSharp/Formats/IImageDecoder.cs

@ -17,10 +17,11 @@ namespace ImageSharp.Formats
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</param>
void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
/// <returns>The decoded image</returns>
Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>;
}
}

7
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -14,15 +14,14 @@ namespace ImageSharp.Formats
public class JpegDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
using (JpegDecoderCore decoder = new JpegDecoderCore(options))
using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration))
{
decoder.Decode(image, stream, false);
return decoder.Decode<TColor>(stream);
}
}
}

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

@ -42,6 +42,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly IDecoderOptions options;
/// <summary>
/// The global configuration
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The App14 marker color-space
/// </summary>
@ -91,8 +96,10 @@ namespace ImageSharp.Formats
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public JpegDecoderCore(IDecoderOptions options)
/// <param name="configuration">The configuration.</param>
public JpegDecoderCore(IDecoderOptions options, Configuration configuration)
{
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new DecoderOptions();
this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
@ -180,18 +187,17 @@ namespace ImageSharp.Formats
/// the data to image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image, where the data should be set to.</param>
/// <param name="stream">The stream, where the image should be.</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param>
public void Decode<TColor>(Image<TColor> image, Stream stream, bool metadataOnly)
/// <returns>The decoded image.</returns>
public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
this.ProcessStream(image, stream, metadataOnly);
if (!metadataOnly)
{
this.ProcessBlocksIntoJpegImageChannels<TColor>();
this.ConvertJpegPixelsToImagePixels(image);
}
ImageMetaData metadata = new ImageMetaData();
this.ProcessStream(metadata, stream, false);
this.ProcessBlocksIntoJpegImageChannels<TColor>();
Image<TColor> image = this.ConvertJpegPixelsToImagePixels<TColor>(metadata);
return image;
}
/// <summary>
@ -276,12 +282,10 @@ namespace ImageSharp.Formats
/// <summary>
/// Read metadata from stream and read the blocks in the scans into <see cref="DecodedBlocks"/>.
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="image">The <see cref="Image{TColor}"/></param>
/// <param name="metadata">The metadata</param>
/// <param name="stream">The stream</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param>
private void ProcessStream<TColor>(Image<TColor> image, Stream stream, bool metadataOnly)
where TColor : struct, IPixel<TColor>
private void ProcessStream(ImageMetaData metadata, Stream stream, bool metadataOnly)
{
this.InputStream = stream;
this.InputProcessor = new InputProcessor(stream, this.Temp);
@ -429,7 +433,7 @@ namespace ImageSharp.Formats
this.ProcessApplicationHeader(remaining);
break;
case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining, image);
this.ProcessApp1Marker(remaining, metadata);
break;
case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining);
@ -496,13 +500,17 @@ namespace ImageSharp.Formats
/// Convert the pixel data in <see cref="YCbCrImage"/> and/or <see cref="JpegPixelArea"/> into pixels of <see cref="Image{TColor}"/>
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="image">The destination image</param>
private void ConvertJpegPixelsToImagePixels<TColor>(Image<TColor> image)
/// <param name="metadata">The metadata for the image.</param>
/// <returns>The decoded image.</returns>
private Image<TColor> ConvertJpegPixelsToImagePixels<TColor>(ImageMetaData metadata)
where TColor : struct, IPixel<TColor>
{
Image<TColor> image = Image.Create<TColor>(this.ImageWidth, this.ImageHeight, metadata, this.configuration);
if (this.grayImage.IsInitialized)
{
this.ConvertFromGrayScale(this.ImageWidth, this.ImageHeight, image);
this.ConvertFromGrayScale(image);
return image;
}
else if (this.ycbcrImage != null)
{
@ -519,27 +527,27 @@ namespace ImageSharp.Formats
// TODO: YCbCrA?
if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck)
{
this.ConvertFromYcck(this.ImageWidth, this.ImageHeight, image);
this.ConvertFromYcck(image);
}
else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
// Assume CMYK
this.ConvertFromCmyk(this.ImageWidth, this.ImageHeight, image);
this.ConvertFromCmyk(image);
}
return;
return image;
}
if (this.ComponentCount == 3)
{
if (this.IsRGB())
{
this.ConvertFromRGB(this.ImageWidth, this.ImageHeight, image);
return;
this.ConvertFromRGB(image);
return image;
}
this.ConvertFromYCbCr(this.ImageWidth, this.ImageHeight, image);
return;
this.ConvertFromYCbCr(image);
return image;
}
throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces.");
@ -582,28 +590,24 @@ namespace ImageSharp.Formats
/// Converts the image from the original CMYK image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromCmyk<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromCmyk<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
y =>
{
// TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte cyan = this.ycbcrImage.YChannel.Pixels[yo + x];
byte magenta = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -623,24 +627,20 @@ namespace ImageSharp.Formats
/// Converts the image from the original grayscale image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromGrayScale<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromGrayScale<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
int yoff = this.grayImage.GetRowOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte rgb = this.grayImage.Pixels[yoff + x];
@ -658,20 +658,17 @@ namespace ImageSharp.Formats
/// Converts the image from the original RBG image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The height.</param>
/// <param name="image">The image.</param>
private void ConvertFromRGB<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromRGB<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
@ -679,7 +676,7 @@ namespace ImageSharp.Formats
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte red = this.ycbcrImage.YChannel.Pixels[yo + x];
byte green = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -699,20 +696,16 @@ namespace ImageSharp.Formats
/// Converts the image from the original YCbCr image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromYCbCr<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromYCbCr<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
@ -720,7 +713,7 @@ namespace ImageSharp.Formats
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte yy = this.ycbcrImage.YChannel.Pixels[yo + x];
byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -740,28 +733,24 @@ namespace ImageSharp.Formats
/// Converts the image from the original YCCK image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromYcck<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromYcck<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
y =>
{
// TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte yy = this.ycbcrImage.YChannel.Pixels[yo + x];
byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -959,11 +948,9 @@ namespace ImageSharp.Formats
/// <summary>
/// Processes the App1 marker retrieving any stored metadata
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <param name="image">The image.</param>
private void ProcessApp1Marker<TColor>(int remaining, Image<TColor> image)
where TColor : struct, IPixel<TColor>
/// <param name="metadata">The image.</param>
private void ProcessApp1Marker(int remaining, ImageMetaData metadata)
{
if (remaining < 6 || this.options.IgnoreMetadata)
{
@ -978,7 +965,7 @@ namespace ImageSharp.Formats
&& profile[5] == '\0')
{
this.isExif = true;
image.MetaData.ExifProfile = new ExifProfile(profile);
metadata.ExifProfile = new ExifProfile(profile);
}
}

12
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -31,25 +31,27 @@ namespace ImageSharp.Formats
public class PngDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options);
this.Decode(image, stream, pngOptions);
return this.Decode<TColor>(configuration, stream, pngOptions);
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</param>
public void Decode<TColor>(Image<TColor> image, Stream stream, IPngDecoderOptions options)
/// <returns>The decoded image.</returns>
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IPngDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
new PngDecoderCore(options).Decode(image, stream);
return new PngDecoderCore(options, configuration).Decode<TColor>(stream);
}
}
}

47
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -74,6 +74,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The stream to decode from.
/// </summary>
@ -134,8 +139,10 @@ namespace ImageSharp.Formats
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public PngDecoderCore(IPngDecoderOptions options)
/// <param name="configuration">The configuration.</param>
public PngDecoderCore(IPngDecoderOptions options, Configuration configuration)
{
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new PngDecoderOptions();
}
@ -148,7 +155,6 @@ namespace ImageSharp.Formats
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param>
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
@ -156,10 +162,11 @@ namespace ImageSharp.Formats
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
public void Decode<TColor>(Image<TColor> image, Stream stream)
/// <returns>The decoded image</returns>
public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
Image<TColor> currentImage = image;
ImageMetaData metadata = new ImageMetaData();
this.currentStream = stream;
this.currentStream.Skip(8);
@ -177,7 +184,7 @@ namespace ImageSharp.Formats
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(currentImage, currentChunk.Data);
this.ReadPhysicalChunk(metadata, currentChunk.Data);
break;
case PngChunkTypes.Data:
dataStream.Write(currentChunk.Data, 0, currentChunk.Length);
@ -186,7 +193,7 @@ namespace ImageSharp.Formats
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length);
this.palette = pal;
image.MetaData.Quality = pal.Length / 3;
metadata.Quality = pal.Length / 3;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
@ -194,7 +201,7 @@ namespace ImageSharp.Formats
this.paletteAlpha = alpha;
break;
case PngChunkTypes.Text:
this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length);
this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -208,17 +215,19 @@ namespace ImageSharp.Formats
}
}
if (this.header.Width > image.MaxWidth || this.header.Height > image.MaxHeight)
if (this.header.Width > Image<TColor>.MaxWidth || this.header.Height > Image<TColor>.MaxHeight)
{
throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image<TColor>.MaxWidth}x{Image<TColor>.MaxHeight}'");
}
image.InitPixels(this.header.Width, this.header.Height);
Image<TColor> image = Image.Create<TColor>(this.header.Width, this.header.Height, metadata, this.configuration);
using (PixelAccessor<TColor> pixels = image.Lock())
{
this.ReadScanlines(dataStream, pixels);
}
return image;
}
}
@ -270,18 +279,16 @@ namespace ImageSharp.Formats
/// <summary>
/// Reads the data chunk containing physical dimension data.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to read to.</param>
/// <param name="metadata">The metadata to read to.</param>
/// <param name="data">The data containing physical data.</param>
private void ReadPhysicalChunk<TColor>(Image<TColor> image, byte[] data)
where TColor : struct, IPixel<TColor>
private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data)
{
data.ReverseBytes(0, 4);
data.ReverseBytes(4, 4);
// 39.3700787 = inches in a meter.
image.MetaData.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d;
image.MetaData.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
metadata.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d;
metadata.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
}
/// <summary>
@ -768,12 +775,10 @@ namespace ImageSharp.Formats
/// <summary>
/// Reads a text chunk containing image properties from the data.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to decode to.</param>
/// <param name="metadata">The metadata to decode to.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
/// <param name="length">The maximum length to read.</param>
private void ReadTextChunk<TColor>(Image<TColor> image, byte[] data, int length)
where TColor : struct, IPixel<TColor>
private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length)
{
if (this.options.IgnoreMetadata)
{
@ -794,7 +799,7 @@ namespace ImageSharp.Formats
string name = this.options.TextEncoding.GetString(data, 0, zeroIndex);
string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
image.MetaData.Properties.Add(new ImageProperty(name, value));
metadata.Properties.Add(new ImageProperty(name, value));
}
/// <summary>

66
src/ImageSharp/Image.Create.cs

@ -0,0 +1,66 @@
// <copyright file="Image.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.Diagnostics;
using System.IO;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Create a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images matadata to preload.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <returns>
/// A new <see cref="Image{TColor}"/> unless <typeparamref name="TColor"/> is <see cref="Color"/> in which case it returns <see cref="Image" />
/// </returns>
internal static Image<TColor> Create<TColor>(int width, int height, ImageMetaData metadata, Configuration configuration)
where TColor : struct, IPixel<TColor>
{
if (typeof(TColor) == typeof(Color))
{
return new Image(width, height, metadata, configuration) as Image<TColor>;
}
else
{
return new Image<TColor>(width, height, metadata, configuration);
}
}
/// <summary>
/// Create a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <returns>
/// A new <see cref="Image{TColor}"/> unless <typeparamref name="TColor"/> is <see cref="Color"/> in which case it returns <see cref="Image" />
/// </returns>
internal static Image<TColor> Create<TColor>(int width, int height, Configuration configuration)
where TColor : struct, IPixel<TColor>
{
return Image.Create<TColor>(width, height, null, configuration);
}
}
}

75
src/ImageSharp/Image.Decode.cs

@ -0,0 +1,75 @@
// <copyright file="Image.Decode.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.Buffers;
using System.IO;
using System.Linq;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// By reading the header on the provided stream this calculates the images format.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="config">The configuration.</param>
/// <returns>The image format or null if none found.</returns>
private static IImageFormat DiscoverFormat(Stream stream, Configuration config)
{
// This is probably a candidate for making into a public API in the future!
int maxHeaderSize = config.MaxHeaderSize;
if (maxHeaderSize <= 0)
{
return null;
}
IImageFormat format;
byte[] header = ArrayPool<byte>.Shared.Rent(maxHeaderSize);
try
{
long startPosition = stream.Position;
stream.Read(header, 0, maxHeaderSize);
stream.Position = startPosition;
format = config.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header));
}
finally
{
ArrayPool<byte>.Shared.Return(header);
}
return format;
}
/// <summary>
/// Decodes the image stream to the current image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream.</param>
/// <param name="options">The options for the decoder.</param>
/// <param name="config">the configuration.</param>
/// <returns>
/// The decoded image
/// </returns>
private static Image<TColor> Decode<TColor>(Stream stream, IDecoderOptions options, Configuration config)
where TColor : struct, IPixel<TColor>
{
IImageFormat format = DiscoverFormat(stream, config);
if (format == null)
{
return null;
}
Image<TColor> img = format.Decoder.Decode<TColor>(config, stream, options);
img.CurrentImageFormat = format;
return img;
}
}
}

212
src/ImageSharp/Image.FromBytes.cs

@ -0,0 +1,212 @@
// <copyright file="Image.FromStream.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.IO;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data)
{
return Load(null, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data, IDecoderOptions options)
{
return Load(null, data, options);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, byte[] data)
{
return Load(config, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data, IImageDecoder decoder)
{
return Load(data, decoder, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, byte[] data, IDecoderOptions options)
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load(config, ms, options);
}
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options)
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load(ms, decoder, options);
}
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, data, options);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, byte[] data)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(config, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data, IImageDecoder decoder)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(data, decoder, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, byte[] data, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load<TColor>(config, ms, options);
}
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data, IImageDecoder decoder, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load<TColor>(ms, decoder, options);
}
}
}
}

214
src/ImageSharp/Image.FromFile.cs

@ -0,0 +1,214 @@
// <copyright file="Image.FromStream.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
#if !NETSTANDARD1_1
using System;
using System.IO;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path)
{
return Load(null, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path, IDecoderOptions options)
{
return Load(null, path, options);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, string path)
{
return Load(config, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path, IImageDecoder decoder)
{
return Load(path, decoder, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options)
{
return new Image(Load<Color>(path, decoder, options));
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, string path, IDecoderOptions options)
{
config = config ?? Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(path))
{
return Load(config, s, options);
}
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, path, options);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The config for the decoder.</param>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, string path)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(config, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path, IImageDecoder decoder)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(path, decoder, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The configuration options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, string path, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
config = config ?? Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(path))
{
return Load<TColor>(config, s, options);
}
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path, IImageDecoder decoder, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
Configuration config = Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(path))
{
return Load<TColor>(s, decoder, options);
}
}
}
#endif
}

246
src/ImageSharp/Image.FromStream.cs

@ -0,0 +1,246 @@
// <copyright file="Image.FromStream.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.IO;
using System.Text;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream)
{
return Load(null, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream, IDecoderOptions options)
{
return Load(null, stream, options);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, Stream stream)
{
return Load(config, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream, IImageDecoder decoder)
{
return Load(stream, decoder, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, Stream stream, IDecoderOptions options)
{
Image<Color> image = Load<Color>(config, stream, options);
return image as Image ?? new Image(image);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options)
{
Image<Color> image = new Image(Load<Color>(stream, decoder, options));
return image as Image ?? new Image(image);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, stream, options);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The config for the decoder.</param>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, Stream stream)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(config, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream, IImageDecoder decoder)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, decoder, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream, IImageDecoder decoder, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return WithSeekableStream(stream, s => decoder.Decode<TColor>(Configuration.Default, s, options));
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The configuration options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
config = config ?? Configuration.Default;
Image<TColor> img = WithSeekableStream(stream, s => Decode<TColor>(stream, options, config));
if (img != null)
{
return img;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
foreach (IImageFormat format in config.ImageFormats)
{
stringBuilder.AppendLine("-" + format);
}
throw new NotSupportedException(stringBuilder.ToString());
}
private static T WithSeekableStream<T>(Stream stream, Func<Stream, T> action)
{
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
if (stream.CanSeek)
{
return action(stream);
}
else
{
// We want to be able to load images from things like HttpContext.Request.Body
using (MemoryStream ms = new MemoryStream())
{
stream.CopyTo(ms);
ms.Position = 0;
return action(stream);
}
}
}
}
}

199
src/ImageSharp/Image.cs

@ -5,6 +5,7 @@
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.IO;
@ -15,7 +16,7 @@ namespace ImageSharp
/// packed into a single unsigned integer value.
/// </summary>
[DebuggerDisplay("Image: {Width}x{Height}")]
public sealed class Image : Image<Color>
public sealed partial class Image : Image<Color>
{
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class
@ -26,201 +27,45 @@ namespace ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
public Image(int width, int height, Configuration configuration = null)
public Image(int width, int height, Configuration configuration)
: base(width, height, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream)
: base(stream, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options)
: base(stream, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, Configuration configuration)
: base(stream, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options, Configuration configuration)
: base(stream, options, configuration)
{
}
#if !NETSTANDARD1_1
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath)
: base(filePath, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options)
: base(filePath, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, Configuration configuration)
: base(filePath, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options, Configuration configuration)
: base(filePath, options, configuration)
{
}
#endif
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes)
: base(bytes, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// Initializes a new instance of the <see cref="Image"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options)
: base(bytes, options, null)
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public Image(int width, int height)
: this(width, height, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// Initializes a new instance of the <see cref="Image"/> class
/// by making a copy from another image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, Configuration configuration)
: base(bytes, null, configuration)
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public Image(Image<Color> other)
: base(other)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// Initializes a new instance of the <see cref="Image"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The metadata.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options, Configuration configuration)
: base(bytes, options, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class
/// by making a copy from another image.
/// </summary>
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public Image(Image other)
: base(other)
internal Image(int width, int height, ImageMetaData metadata, Configuration configuration)
: base(width, height, metadata, configuration)
{
}
}

10
src/ImageSharp/Image/IImageBase.cs

@ -15,16 +15,6 @@ namespace ImageSharp
/// </summary>
Rectangle Bounds { get; }
/// <summary>
/// Gets or sets the maximum allowable width in pixels.
/// </summary>
int MaxWidth { get; set; }
/// <summary>
/// Gets or sets the maximum allowable height in pixels.
/// </summary>
int MaxHeight { get; set; }
/// <summary>
/// Gets the width in pixels.
/// </summary>

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

@ -21,16 +21,6 @@ namespace ImageSharp
/// </summary>
TColor[] Pixels { get; }
/// <summary>
/// Sets the size of the pixel array of the image to the given width and height.
/// </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>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
void InitPixels(int width, int height);
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>

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

@ -18,6 +18,16 @@ namespace ImageSharp
public abstract class ImageBase<TColor> : IImageBase<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>
/// Gets or sets the maximum allowable width in pixels.
/// </summary>
public const int MaxWidth = int.MaxValue;
/// <summary>
/// Gets or sets the maximum allowable height in pixels.
/// </summary>
public const int MaxHeight = int.MaxValue;
/// <summary>
/// The image pixels
/// </summary>
@ -40,7 +50,7 @@ namespace ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
protected ImageBase(Configuration configuration = null)
protected ImageBase(Configuration configuration)
{
this.Configuration = configuration ?? Configuration.Default;
}
@ -56,10 +66,15 @@ namespace ImageSharp
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
protected ImageBase(int width, int height, Configuration configuration = null)
protected ImageBase(int width, int height, Configuration configuration)
: this(configuration)
{
this.Configuration = configuration ?? Configuration.Default;
this.InitPixels(width, height);
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width;
this.Height = height;
this.RentPixels();
this.ClearPixels();
}
@ -73,6 +88,7 @@ namespace ImageSharp
/// Thrown if the given <see cref="ImageBase{TColor}"/> is null.
/// </exception>
protected ImageBase(ImageBase<TColor> other)
: this(other.Configuration)
{
Guard.NotNull(other, nameof(other), "Other image cannot be null.");
@ -90,12 +106,6 @@ namespace ImageSharp
}
}
/// <inheritdoc/>
public int MaxWidth { get; set; } = int.MaxValue;
/// <inheritdoc/>
public int MaxHeight { get; set; } = int.MaxValue;
/// <inheritdoc/>
public TColor[] Pixels => this.pixelBuffer;
@ -139,17 +149,6 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
/// <inheritdoc/>
public void InitPixels(int width, int height)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width;
this.Height = height;
this.RentPixels();
}
/// <inheritdoc/>
public PixelAccessor<TColor> Lock()
{

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

@ -35,211 +35,20 @@ namespace ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
public Image(int width, int height, Configuration configuration = null)
: base(width, height, configuration)
{
if (!this.Configuration.ImageFormats.Any())
{
throw new InvalidOperationException("No image formats have been configured.");
}
this.CurrentImageFormat = this.Configuration.ImageFormats.First();
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream)
: this(stream, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options)
: this(stream, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, Configuration configuration)
: this(stream, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(stream, nameof(stream));
this.Load(stream, options);
}
#if !NETSTANDARD1_1
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath)
: this(filePath, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options)
: this(filePath, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, Configuration configuration)
: this(filePath, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(filePath, nameof(filePath));
using (Stream fs = this.Configuration.FileSystem.OpenRead(filePath))
{
this.Load(fs, options);
}
}
#endif
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes)
: this(bytes, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options)
: this(bytes, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, Configuration configuration)
: this(bytes, null, configuration)
public Image(int width, int height, Configuration configuration)
: this(width, height, new ImageMetaData(), configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// Initializes a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options, Configuration configuration)
: base(configuration)
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public Image(int width, int height)
: this(width, height, null)
{
Guard.NotNull(bytes, nameof(bytes));
using (MemoryStream stream = new MemoryStream(bytes, false))
{
this.Load(stream, options);
}
}
/// <summary>
@ -271,13 +80,35 @@ namespace ImageSharp
public Image(ImageBase<TColor> other)
: base(other)
{
this.CopyProperties(other);
this.MetaData = new ImageMetaData();
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
internal Image(int width, int height, ImageMetaData metadata, Configuration configuration)
: base(width, height, configuration)
{
if (!this.Configuration.ImageFormats.Any())
{
throw new InvalidOperationException("No image formats have been configured.");
}
this.MetaData = metadata ?? new ImageMetaData();
this.CurrentImageFormat = this.Configuration.ImageFormats.First();
}
/// <summary>
/// Gets the meta data of the image.
/// </summary>
public ImageMetaData MetaData { get; private set; } = new ImageMetaData();
public ImageMetaData MetaData { get; private set; }
/// <summary>
/// Gets the width of the image in inches. It is calculated as the width of the image
@ -588,103 +419,8 @@ namespace ImageSharp
/// </param>
private void CopyProperties(IImage other)
{
base.CopyProperties(other);
this.CurrentImageFormat = other.CurrentImageFormat;
this.MetaData = new ImageMetaData(other.MetaData);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
private void Load(Stream stream, IDecoderOptions options)
{
if (!this.Configuration.ImageFormats.Any())
{
throw new InvalidOperationException("No image formats have been configured.");
}
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
if (stream.CanSeek)
{
if (this.Decode(stream, options))
{
return;
}
}
else
{
// We want to be able to load images from things like HttpContext.Request.Body
using (MemoryStream ms = new MemoryStream())
{
stream.CopyTo(ms);
ms.Position = 0;
if (this.Decode(ms, options))
{
return;
}
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
foreach (IImageFormat format in this.Configuration.ImageFormats)
{
stringBuilder.AppendLine("-" + format);
}
throw new NotSupportedException(stringBuilder.ToString());
}
/// <summary>
/// Decodes the image stream to the current image.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="options">The options for the decoder.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
private bool Decode(Stream stream, IDecoderOptions options)
{
int maxHeaderSize = this.Configuration.MaxHeaderSize;
if (maxHeaderSize <= 0)
{
return false;
}
IImageFormat format;
byte[] header = ArrayPool<byte>.Shared.Rent(maxHeaderSize);
try
{
long startPosition = stream.Position;
stream.Read(header, 0, maxHeaderSize);
stream.Position = startPosition;
format = this.Configuration.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header));
}
finally
{
ArrayPool<byte>.Shared.Return(header);
}
if (format == null)
{
return false;
}
format.Decoder.Decode(this, stream, options);
this.CurrentImageFormat = format;
return true;
}
}
}

2
src/ImageSharp/ImageSharp.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<Description>A cross-platform library for the processing of image files; written in C#</Description>
<AssemblyTitle>ImageSharp</AssemblyTitle>
<VersionPrefix>1.0.0-alpha4</VersionPrefix>
<VersionPrefix>1.0.0-alpha5</VersionPrefix>
<Authors>James Jackson-South and contributors</Authors>
<TargetFrameworks>netstandard1.3;netstandard1.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

9
src/ImageSharp/MetaData/ImageMetaData.cs

@ -5,6 +5,7 @@
namespace ImageSharp
{
using System;
using System.Collections.Generic;
/// <summary>
@ -55,12 +56,16 @@ namespace ImageSharp
foreach (ImageProperty property in other.Properties)
{
this.Properties.Add(new ImageProperty(property));
this.Properties.Add(new ImageProperty(property));
}
if (other.ExifProfile != null)
{
this.ExifProfile = new ExifProfile(other.ExifProfile);
this.ExifProfile = new ExifProfile(other.ExifProfile);
}
else
{
this.ExifProfile = null;
}
}

2
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -137,7 +137,7 @@ namespace ImageSharp
using (MemoryStream memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength))
{
return new Image<TColor>(memStream);
return Image.Load<TColor>(memStream);
}
}

7
src/README.md

@ -0,0 +1,7 @@
# ImageSharp core libraries
## Coding conventions
### Argument ordering
- When passing a `Configuration` object it should be the first argument

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

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

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

@ -31,7 +31,7 @@ namespace ImageSharp.Benchmarks.Image
private Size LoadPng(MemoryStream stream)
{
using (Image image = new Image(stream))
using (Image image = Image.Load(stream))
{
return new Size(image.Width, image.Height);
}

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

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

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

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

2
tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs

@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image
public void DecodeJpegImageSharp()
{
this.ForEachStream(
ms => new ImageSharp.Image(ms)
ms => ImageSharp.Image.Load(ms)
);
}

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

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

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

@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image
if (this.bmpStream == null)
{
this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp");
this.bmpCore = new CoreImage(this.bmpStream);
this.bmpCore = CoreImage.Load(this.bmpStream);
this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream);
}

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

@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image
if (this.bmpStream == null)
{
this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp");
this.bmpCore = new CoreImage(this.bmpStream);
this.bmpCore = CoreImage.Load(this.bmpStream);
this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream);
}

2
tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs

@ -34,7 +34,7 @@ namespace ImageSharp.Benchmarks.Image
? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg"
: "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
this.bmpStream = File.OpenRead(path);
this.bmpCore = new Image(this.bmpStream);
this.bmpCore = Image.Load(this.bmpStream);
this.bmpStream.Position = 0;
}
}

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

@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image
if (this.bmpStream == null)
{
this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp");
this.bmpCore = new CoreImage(this.bmpStream);
this.bmpCore = CoreImage.Load(this.bmpStream);
this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream);
}

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

@ -38,7 +38,7 @@ namespace ImageSharp.Benchmarks.Image
? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg"
: "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
this.bmpStream = File.OpenRead(path);
this.bmpCore = new CoreImage(this.bmpStream);
this.bmpCore = CoreImage.Load(this.bmpStream);
this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream);
}

2
tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs

@ -154,7 +154,7 @@ namespace ImageSharp.Benchmarks.Image
using (MemoryStream ms1 = new MemoryStream(bytes))
{
this.FileNamesToImageSharpImages[fn] = new Image(ms1);
this.FileNamesToImageSharpImages[fn] = Image.Load(ms1);
}

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

@ -23,7 +23,7 @@ namespace ImageSharp.Benchmarks
{
using (FileStream stream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"))
{
this.image = new CoreImage(stream);
this.image = CoreImage.Load(stream);
}
}
}

27
tests/ImageSharp.Tests/Drawing/Text/DrawText.cs

@ -219,5 +219,32 @@ namespace ImageSharp.Tests.Drawing.Text
Assert.IsType<FillRegionProcessor<Color>>(this.img.ProcessorApplications[0].processor);
Assert.IsType<DrawPathProcessor<Color>>(this.img.ProcessorApplications[1].processor);
}
[Fact]
public void GlyphHeightChangesBasedOnuseImageResolutionFlag()
{
this.img.MetaData.VerticalResolution = 1;
this.img.MetaData.HorizontalResolution = 1;
this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true) {
UseImageResolution = false
});
this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true)
{
UseImageResolution = true
});
Assert.NotEmpty(this.img.ProcessorApplications);
Assert.Equal(2, this.img.ProcessorApplications.Count);
FillRegionProcessor<Color> ownResolution = Assert.IsType<FillRegionProcessor<Color>>(this.img.ProcessorApplications[0].processor);
FillRegionProcessor<Color> imgResolution = Assert.IsType<FillRegionProcessor<Color>>(this.img.ProcessorApplications[1].processor);
ShapeRegion ownRegion = Assert.IsType<ShapeRegion>(ownResolution.Region);
ShapeRegion imgRegion = Assert.IsType<ShapeRegion>(imgResolution.Region);
// magic numbers based on the font used at well known resolutions
Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2);
Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2);
}
}
}

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

@ -150,7 +150,7 @@ namespace ImageSharp.Tests
serialized = memoryStream.ToArray();
}
using (Image image2 = new Image(serialized))
using (Image image2 = Image.Load(serialized))
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{
image2.Save(output);

6
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -29,7 +29,7 @@ namespace ImageSharp.Tests
input.Save(memStream, new GifFormat(), options);
memStream.Position = 0;
using (Image output = new Image(memStream))
using (Image output = Image.Load(memStream))
{
Assert.Equal(1, output.MetaData.Properties.Count);
Assert.Equal("Comments", output.MetaData.Properties[0].Name);
@ -56,7 +56,7 @@ namespace ImageSharp.Tests
input.SaveAsGif(memStream, options);
memStream.Position = 0;
using (Image output = new Image(memStream))
using (Image output = Image.Load(memStream))
{
Assert.Equal(0, output.MetaData.Properties.Count);
}
@ -77,7 +77,7 @@ namespace ImageSharp.Tests
input.Save(memStream, new GifFormat());
memStream.Position = 0;
using (Image output = new Image(memStream))
using (Image output = Image.Load(memStream))
{
Assert.Equal(1, output.MetaData.Properties.Count);
Assert.Equal("Comments", output.MetaData.Properties[0].Name);

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

@ -89,11 +89,10 @@ namespace ImageSharp.Tests
{
image.Save(ms, new JpegEncoder());
ms.Seek(0, SeekOrigin.Begin);
Image<TColor> mirror = provider.Factory.CreateImage(1, 1);
using (JpegDecoderCore decoder = new JpegDecoderCore(null))
using (JpegDecoderCore decoder = new JpegDecoderCore(null, null))
{
decoder.Decode(mirror, ms, true);
Image<TColor> mirror = decoder.Decode<TColor>(ms);
Assert.Equal(decoder.ImageWidth, image.Width);
Assert.Equal(decoder.ImageHeight, image.Height);

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

@ -88,7 +88,7 @@ namespace ImageSharp.Tests
input.Save(memStream, new JpegFormat(), options);
memStream.Position = 0;
using (Image output = new Image(memStream))
using (Image output = Image.Load(memStream))
{
Assert.NotNull(output.MetaData.ExifProfile);
}
@ -113,7 +113,7 @@ namespace ImageSharp.Tests
input.SaveAsJpeg(memStream, options);
memStream.Position = 0;
using (Image output = new Image(memStream))
using (Image output = Image.Load(memStream))
{
Assert.Null(output.MetaData.ExifProfile);
}

2
tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

@ -50,7 +50,7 @@ namespace ImageSharp.Tests
ExecutionCount,
() =>
{
Image img = new Image(bytes);
Image img = Image.Load(bytes);
},
// ReSharper disable once ExplicitCallerInfoArgument
$"Decode {fileName}");

520
tests/ImageSharp.Tests/Image/ImageLoadTests.cs

@ -0,0 +1,520 @@
// <copyright file="PixelAccessorTests.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;
using System.IO;
using System.Linq;
using ImageSharp.Formats;
using ImageSharp.IO;
using Moq;
using Xunit;
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class ImageLoadTests : IDisposable
{
private readonly Mock<IFileSystem> fileSystem;
private readonly IDecoderOptions decoderOptions;
private Image<Color> returnImage;
private Mock<IImageDecoder> localDecoder;
private Mock<IImageFormat> localFormat;
private readonly string FilePath;
public Configuration LocalConfiguration { get; private set; }
public byte[] Marker { get; private set; }
public MemoryStream DataStream { get; private set; }
public byte[] DecodedData { get; private set; }
public ImageLoadTests()
{
this.returnImage = new Image(1, 1);
this.localDecoder = new Mock<IImageDecoder>();
this.localFormat = new Mock<IImageFormat>();
this.localFormat.Setup(x => x.Decoder).Returns(this.localDecoder.Object);
this.localFormat.Setup(x => x.Encoder).Returns(new Mock<IImageEncoder>().Object);
this.localFormat.Setup(x => x.MimeType).Returns("img/test");
this.localFormat.Setup(x => x.Extension).Returns("png");
this.localFormat.Setup(x => x.HeaderSize).Returns(1);
this.localFormat.Setup(x => x.IsSupportedFileFormat(It.IsAny<byte[]>())).Returns(true);
this.localFormat.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" });
this.localDecoder.Setup(x => x.Decode<Color>(It.IsAny<Configuration>(), It.IsAny<Stream>(), It.IsAny<IDecoderOptions>()))
.Callback<Configuration, Stream, IDecoderOptions>((c, s, o) => {
using (var ms = new MemoryStream())
{
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
}
})
.Returns(this.returnImage);
this.fileSystem = new Mock<IFileSystem>();
this.LocalConfiguration = new Configuration(this.localFormat.Object)
{
FileSystem = this.fileSystem.Object
};
TestFormat.RegisterGloablTestFormat();
this.Marker = Guid.NewGuid().ToByteArray();
this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker);
this.decoderOptions = new Mock<IDecoderOptions>().Object;
this.FilePath = Guid.NewGuid().ToString();
this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream);
TestFileSystem.RegisterGloablTestFormat();
TestFileSystem.Global.AddFile(this.FilePath, this.DataStream);
}
[Fact]
public void LoadFromStream()
{
Image img = Image.Load(this.DataStream);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithType()
{
Image<Color> img = Image.Load<Color>(this.DataStream);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Color>(), img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithOptions()
{
Image img = Image.Load(this.DataStream, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithTypeAndOptions()
{
Image<Color> img = Image.Load<Color>(this.DataStream, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Color>(), img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithConfig()
{
Stream stream = new MemoryStream();
Image img = Image.Load(this.LocalConfiguration, stream);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, stream, null));
}
[Fact]
public void LoadFromStreamWithTypeAndConfig()
{
Stream stream = new MemoryStream();
Image<Color> img = Image.Load<Color>(this.LocalConfiguration, stream);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, stream, null));
}
[Fact]
public void LoadFromStreamWithConfigAndOptions()
{
Stream stream = new MemoryStream();
Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, stream, this.decoderOptions));
}
[Fact]
public void LoadFromStreamWithTypeAndConfigAndOptions()
{
Stream stream = new MemoryStream();
Image<Color> img = Image.Load<Color>(this.LocalConfiguration, stream, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, stream, this.decoderOptions));
}
[Fact]
public void LoadFromStreamWithDecoder()
{
Stream stream = new MemoryStream();
Image img = Image.Load(stream, this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, stream, null));
}
[Fact]
public void LoadFromStreamWithTypeAndDecoder()
{
Stream stream = new MemoryStream();
Image<Color> img = Image.Load<Color>(stream, this.localDecoder.Object);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, stream, null));
}
[Fact]
public void LoadFromStreamWithDecoderAndOptions()
{
Stream stream = new MemoryStream();
Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, stream, this.decoderOptions));
}
[Fact]
public void LoadFromStreamWithTypeAndDecoderAndOptions()
{
Stream stream = new MemoryStream();
Image<Color> img = Image.Load<Color>(stream, this.localDecoder.Object, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, stream, this.decoderOptions));
}
[Fact]
public void LoadFromBytes()
{
Image img = Image.Load(this.DataStream.ToArray());
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default);
}
[Fact]
public void LoadFromBytesWithType()
{
Image<Color> img = Image.Load<Color>(this.DataStream.ToArray());
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Color>(), img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default);
}
[Fact]
public void LoadFromBytesWithOptions()
{
Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default);
}
[Fact]
public void LoadFromBytesWithTypeAndOptions()
{
Image<Color> img = Image.Load<Color>(this.DataStream.ToArray(), this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Color>(), img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default);
}
[Fact]
public void LoadFromBytesWithConfig()
{
Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray());
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, It.IsAny<Stream>(), null));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndConfig()
{
Image<Color> img = Image.Load<Color>(this.LocalConfiguration, this.DataStream.ToArray());
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, It.IsAny<Stream>(), null));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithConfigAndOptions()
{
Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, It.IsAny<Stream>(), this.decoderOptions));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndConfigAndOptions()
{
Image<Color> img = Image.Load<Color>(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, It.IsAny<Stream>(), this.decoderOptions));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithDecoder()
{
Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, It.IsAny<Stream>(), null));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndDecoder()
{
Image<Color> img = Image.Load<Color>(this.DataStream.ToArray(), this.localDecoder.Object);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, It.IsAny<Stream>(), null));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithDecoderAndOptions()
{
Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, It.IsAny<Stream>(), this.decoderOptions));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndDecoderAndOptions()
{
Image<Color> img = Image.Load<Color>(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, It.IsAny<Stream>(), this.decoderOptions));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromFile()
{
Image img = Image.Load(this.DataStream);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default);
}
[Fact]
public void LoadFromFileWithType()
{
Image<Color> img = Image.Load<Color>(this.DataStream);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Color>(), img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default);
}
[Fact]
public void LoadFromFileWithOptions()
{
Image img = Image.Load(this.DataStream, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default);
}
[Fact]
public void LoadFromFileWithTypeAndOptions()
{
Image<Color> img = Image.Load<Color>(this.DataStream, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Color>(), img);
Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default);
}
[Fact]
public void LoadFromFileWithConfig()
{
Image img = Image.Load(this.LocalConfiguration, this.FilePath);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, this.DataStream, null));
}
[Fact]
public void LoadFromFileWithTypeAndConfig()
{
Image<Color> img = Image.Load<Color>(this.LocalConfiguration, this.FilePath);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, this.DataStream, null));
}
[Fact]
public void LoadFromFileWithConfigAndOptions()
{
Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, this.DataStream, this.decoderOptions));
}
[Fact]
public void LoadFromFileWithTypeAndConfigAndOptions()
{
Image<Color> img = Image.Load<Color>(this.LocalConfiguration, this.FilePath, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.LocalConfiguration, this.DataStream, this.decoderOptions));
}
[Fact]
public void LoadFromFileWithDecoder()
{
Image img = Image.Load(this.FilePath, this.localDecoder.Object);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, this.DataStream, null));
}
[Fact]
public void LoadFromFileWithTypeAndDecoder()
{
Image<Color> img = Image.Load<Color>(this.FilePath, this.localDecoder.Object);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, this.DataStream, null));
}
[Fact]
public void LoadFromFileWithDecoderAndOptions()
{
Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions);
Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, this.DataStream, this.decoderOptions));
}
[Fact]
public void LoadFromFileWithTypeAndDecoderAndOptions()
{
Image<Color> img = Image.Load<Color>(this.FilePath, this.localDecoder.Object, this.decoderOptions);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Color>(Configuration.Default, this.DataStream, this.decoderOptions));
}
public void Dispose()
{
// clean up the global object;
this.returnImage?.Dispose();
}
}
}

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

@ -21,11 +21,11 @@ namespace ImageSharp.Tests
{
Assert.Throws<ArgumentNullException>(() =>
{
new Image((byte[])null);
Image.Load((byte[])null);
});
TestFile file = TestFile.Create(TestImages.Bmp.Car);
using (Image image = new Image(file.Bytes))
using (Image image = Image.Load(file.Bytes))
{
Assert.Equal(600, image.Width);
Assert.Equal(450, image.Height);
@ -36,7 +36,7 @@ namespace ImageSharp.Tests
public void ConstructorFileSystem()
{
TestFile file = TestFile.Create(TestImages.Bmp.Car);
using (Image image = new Image(file.FilePath))
using (Image image = Image.Load(file.FilePath))
{
Assert.Equal(600, image.Width);
Assert.Equal(450, image.Height);
@ -49,7 +49,7 @@ namespace ImageSharp.Tests
System.IO.FileNotFoundException ex = Assert.Throws<System.IO.FileNotFoundException>(
() =>
{
new Image(Guid.NewGuid().ToString());
Image.Load(Guid.NewGuid().ToString());
});
}
@ -59,7 +59,7 @@ namespace ImageSharp.Tests
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(
() =>
{
new Image((string) null);
Image.Load((string) null);
});
}

6
tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs

@ -73,7 +73,7 @@ namespace ImageSharp.Tests
image.SaveAsJpeg(memStream);
memStream.Position = 0;
image = new Image(memStream);
image = Image.Load(memStream);
profile = image.MetaData.ExifProfile;
Assert.NotNull(profile);
@ -91,7 +91,7 @@ namespace ImageSharp.Tests
image.SaveAsJpeg(memStream);
memStream.Position = 0;
image = new Image(memStream);
image = Image.Load(memStream);
profile = image.MetaData.ExifProfile;
Assert.NotNull(profile);
@ -286,7 +286,7 @@ namespace ImageSharp.Tests
image.Dispose();
memStream.Position = 0;
return new Image(memStream);
return Image.Load(memStream);
}
}

4
tests/ImageSharp.Tests/TestFile.cs

@ -46,7 +46,7 @@ namespace ImageSharp.Tests
this.file = file;
this.Bytes = File.ReadAllBytes(file);
this.image = new Image(this.Bytes);
this.image = Image.Load(this.Bytes);
}
/// <summary>
@ -139,7 +139,7 @@ namespace ImageSharp.Tests
/// </returns>
public Image CreateImage(IDecoderOptions options)
{
return new Image(this.Bytes, options);
return Image.Load(this.Bytes, options);
}
/// <summary>

71
tests/ImageSharp.Tests/TestFileSystem.cs

@ -0,0 +1,71 @@
// <copyright file="TestFileSystem.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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using ImageSharp.Formats;
using Xunit;
/// <summary>
/// A test image file.
/// </summary>
public class TestFileSystem : ImageSharp.IO.IFileSystem
{
public static TestFileSystem Global { get; } = new TestFileSystem();
public static void RegisterGloablTestFormat()
{
Configuration.Default.FileSystem = Global;
}
Dictionary<string, Stream> fileSystem = new Dictionary<string, Stream>(StringComparer.OrdinalIgnoreCase);
public void AddFile(string path, Stream data)
{
fileSystem.Add(path, data);
}
public Stream Create(string path)
{
// if we have injected a fake file use it instead
lock (fileSystem)
{
if (fileSystem.ContainsKey(path))
{
Stream stream = fileSystem[path];
stream.Position = 0;
return stream;
}
}
return File.Create(path);
}
public Stream OpenRead(string path)
{
// if we have injected a fake file use it instead
lock (fileSystem)
{
if (fileSystem.ContainsKey(path))
{
Stream stream = fileSystem[path];
stream.Position = 0;
return stream;
}
}
return File.OpenRead(path);
}
}
}

185
tests/ImageSharp.Tests/TestFormat.cs

@ -0,0 +1,185 @@
// <copyright file="TestImage.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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using ImageSharp.Formats;
using Xunit;
/// <summary>
/// A test image file.
/// </summary>
public class TestFormat : ImageSharp.Formats.IImageFormat
{
public static TestFormat GlobalTestFormat { get; } = new TestFormat();
public static void RegisterGloablTestFormat()
{
Configuration.Default.AddImageFormat(GlobalTestFormat);
}
public TestFormat()
{
this.Encoder = new TestEncoder(this); ;
this.Decoder = new TestDecoder(this); ;
}
public List<DecodeOperation> DecodeCalls { get; } = new List<DecodeOperation>();
public IImageEncoder Encoder { get; }
public IImageDecoder Decoder { get; }
private byte[] header = Guid.NewGuid().ToByteArray();
public MemoryStream CreateStream(byte[] marker = null)
{
MemoryStream ms = new MemoryStream();
byte[] data = this.header;
ms.Write(data, 0, data.Length);
if (marker != null)
{
ms.Write(marker, 0, marker.Length);
}
ms.Position = 0;
return ms;
}
Dictionary<Type, object> _sampleImages = new Dictionary<Type, object>();
public void VerifyDecodeCall(byte[] marker, IDecoderOptions options, Configuration config)
{
DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, options, config)).ToArray();
Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend");
foreach (DecodeOperation d in discovered) {
this.DecodeCalls.Remove(d);
}
}
public Image<TColor> Sample<TColor>()
where TColor : struct, IPixel<TColor>
{
lock (this._sampleImages)
{
if (!this._sampleImages.ContainsKey(typeof(TColor)))
{
this._sampleImages.Add(typeof(TColor), new Image<TColor>(1, 1));
}
return (Image<TColor>)this._sampleImages[typeof(TColor)];
}
}
public string MimeType => "img/test";
public string Extension => "test_ext";
public IEnumerable<string> SupportedExtensions => new[] { "test_ext" };
public int HeaderSize => this.header.Length;
public bool IsSupportedFileFormat(byte[] header)
{
if (header.Length < this.header.Length)
{
return false;
}
for (int i = 0; i < this.header.Length; i++)
{
if (header[i] != this.header[i])
{
return false;
}
}
return true;
}
public struct DecodeOperation
{
public byte[] marker;
public IDecoderOptions options;
internal Configuration config;
public bool IsMatch(byte[] testMarker, IDecoderOptions testOptions, Configuration config)
{
if (this.options != testOptions)
{
return false;
}
if (this.config != config)
{
return false;
}
if (testMarker.Length != this.marker.Length)
{
return false;
}
for (int i = 0; i < this.marker.Length; i++)
{
if (testMarker[i] != this.marker[i])
{
return false;
}
}
return true;
}
}
public class TestDecoder : ImageSharp.Formats.IImageDecoder
{
private TestFormat testFormat;
public TestDecoder(TestFormat testFormat)
{
this.testFormat = testFormat;
}
public Image<TColor> Decode<TColor>(Configuration config, Stream stream, IDecoderOptions options) where TColor : struct, IPixel<TColor>
{
var ms = new MemoryStream();
stream.CopyTo(ms);
var marker = ms.ToArray().Skip(this.testFormat.header.Length).ToArray();
this.testFormat.DecodeCalls.Add(new DecodeOperation
{
marker = marker,
options = options,
config = config
});
// TODO record this happend so we an verify it.
return this.testFormat.Sample<TColor>();
}
}
public class TestEncoder : ImageSharp.Formats.IImageEncoder
{
private TestFormat testFormat;
public TestEncoder(TestFormat testFormat)
{
this.testFormat = testFormat;
}
public void Encode<TColor>(Image<TColor> image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel<TColor>
{
// TODO record this happend so we an verify it.
}
}
}
}

2
tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs

@ -21,7 +21,7 @@ namespace ImageSharp.Tests
public virtual Image<TColor> CreateImage(byte[] bytes)
{
return new Image<TColor>(bytes);
return Image.Load<TColor>(bytes);
}
public virtual Image<TColor> CreateImage(Image<TColor> other)

2
tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs

@ -7,7 +7,7 @@ namespace ImageSharp.Tests
{
public class ImageFactory : GenericFactory<Color>
{
public override Image<Color> CreateImage(byte[] bytes) => new Image(bytes);
public override Image<Color> CreateImage(byte[] bytes) => Image.Load(bytes);
public override Image<Color> CreateImage(int width, int height) => new Image(width, height);

Loading…
Cancel
Save