Browse Source

Move image loading out of constructors and into static methods

pull/143/head
Scott Williams 9 years ago
parent
commit
a3185fd6e7
  1. 2
      ImageSharp.sln
  2. 5
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  3. 15
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  4. 10
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  5. 48
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  6. 4
      src/ImageSharp/Formats/IImageDecoder.cs
  7. 5
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  8. 120
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  9. 10
      src/ImageSharp/Formats/Png/PngDecoder.cs
  10. 39
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  11. 67
      src/ImageSharp/Image.Decode.cs
  12. 148
      src/ImageSharp/Image.FromBytes.cs
  13. 148
      src/ImageSharp/Image.FromFile.cs
  14. 184
      src/ImageSharp/Image.FromStream.cs
  15. 190
      src/ImageSharp/Image.cs
  16. 10
      src/ImageSharp/Image/IImageBase.cs
  17. 10
      src/ImageSharp/Image/IImageBase{TColor}.cs
  18. 41
      src/ImageSharp/Image/ImageBase{TColor}.cs
  19. 296
      src/ImageSharp/Image/Image{TColor}.cs
  20. 44
      src/ImageSharp/MetaData/ImageMetaData.cs
  21. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  22. 2
      tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs
  23. 2
      tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs
  24. 2
      tests/ImageSharp.Benchmarks/Image/DecodeGif.cs
  25. 2
      tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs
  26. 2
      tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs
  27. 2
      tests/ImageSharp.Benchmarks/Image/DecodePng.cs
  28. 2
      tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
  29. 2
      tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
  30. 2
      tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs
  31. 2
      tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs
  32. 2
      tests/ImageSharp.Benchmarks/Image/EncodePng.cs
  33. 2
      tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs
  34. 2
      tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs
  35. 2
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  36. 6
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  37. 5
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  38. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  39. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs
  40. 341
      tests/ImageSharp.Tests/Image/ImageLoadTests.cs
  41. 10
      tests/ImageSharp.Tests/Image/ImageTests.cs
  42. 6
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
  43. 4
      tests/ImageSharp.Tests/TestFile.cs
  44. 174
      tests/ImageSharp.Tests/TestFormat.cs
  45. 2
      tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs
  46. 2
      tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs

2
ImageSharp.sln

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26228.4 VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject

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

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

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

@ -48,16 +48,13 @@ namespace ImageSharp.Formats
/// the data to image. /// the data to image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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 /// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param> /// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException"> /// <exception cref="System.ArgumentNullException">
/// <para><paramref name="image"/> is null.</para>
/// <para>- or -</para>
/// <para><paramref name="stream"/> is null.</para> /// <para><paramref name="stream"/> is null.</para>
/// </exception> /// </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> where TColor : struct, IPixel<TColor>
{ {
this.currentStream = stream; this.currentStream = stream;
@ -110,14 +107,14 @@ namespace ImageSharp.Formats
this.currentStream.Read(palette, 0, colorMapSize); 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( throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " $"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 = new Image<TColor>(this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
@ -151,6 +148,8 @@ namespace ImageSharp.Formats
throw new NotSupportedException("Does not support this kind of bitmap files."); throw new NotSupportedException("Does not support this kind of bitmap files.");
} }
} }
return image;
} }
catch (IndexOutOfRangeException e) catch (IndexOutOfRangeException e)
{ {

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

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

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

@ -27,11 +27,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private readonly IGifDecoderOptions options; private readonly IGifDecoderOptions options;
/// <summary>
/// The image to decode the information to.
/// </summary>
private Image<TColor> decodedImage;
/// <summary> /// <summary>
/// The currently loaded stream. /// The currently loaded stream.
/// </summary> /// </summary>
@ -67,6 +62,16 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private GifGraphicsControlExtension graphicsControlExtension; private GifGraphicsControlExtension graphicsControlExtension;
/// <summary>
/// The metadata
/// </summary>
private ImageMetaData metaData;
/// <summary>
/// The image to decode the information to.
/// </summary>
private Image<TColor> image;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TColor}"/> class. /// Initializes a new instance of the <see cref="GifDecoderCore{TColor}"/> class.
/// </summary> /// </summary>
@ -79,13 +84,13 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
/// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </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 try
{ {
this.decodedImage = image; this.metaData = new ImageMetaData();
this.currentStream = stream; this.currentStream = stream;
@ -144,6 +149,8 @@ namespace ImageSharp.Formats
ArrayPool<byte>.Shared.Return(this.globalColorTable); ArrayPool<byte>.Shared.Return(this.globalColorTable);
} }
} }
return this.image;
} }
/// <summary> /// <summary>
@ -212,11 +219,13 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); 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( 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> /// <summary>
@ -261,7 +270,7 @@ namespace ImageSharp.Formats
{ {
this.currentStream.Read(commentsBuffer, 0, length); this.currentStream.Read(commentsBuffer, 0, length);
string comments = this.options.TextEncoding.GetString(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 finally
{ {
@ -343,14 +352,15 @@ namespace ImageSharp.Formats
if (this.previousFrame == null) 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 initializes the image to become fully transparent because the alpha channel is zero.
this.decodedImage.InitPixels(imageWidth, imageHeight); this.image = new Image<TColor>(imageWidth, imageHeight);
this.image.MetaData.LoadFrom(this.metaData);
this.SetFrameDelay(this.decodedImage.MetaData); this.SetFrameDelay(this.metaData);
image = this.decodedImage; image = this.image;
} }
else else
{ {
@ -368,7 +378,7 @@ namespace ImageSharp.Formats
this.RestoreToBackground(image); this.RestoreToBackground(image);
this.decodedImage.Frames.Add(currentFrame); this.image.Frames.Add(currentFrame);
} }
int i = 0; int i = 0;
@ -441,7 +451,7 @@ namespace ImageSharp.Formats
return; return;
} }
this.previousFrame = currentFrame == null ? this.decodedImage.ToFrame() : currentFrame; this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame;
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -462,8 +472,8 @@ namespace ImageSharp.Formats
} }
// Optimization for when the size of the frame is the same as the image size. // Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.decodedImage.Width && if (this.restoreArea.Value.Width == this.image.Width &&
this.restoreArea.Value.Height == this.decodedImage.Height) this.restoreArea.Value.Height == this.image.Height)
{ {
using (PixelAccessor<TColor> pixelAccessor = frame.Lock()) using (PixelAccessor<TColor> pixelAccessor = frame.Lock())
{ {

4
src/ImageSharp/Formats/IImageDecoder.cs

@ -17,10 +17,10 @@ namespace ImageSharp.Formats
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>. /// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</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>(Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>; where TColor : struct, IPixel<TColor>;
} }
} }

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

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

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

@ -180,18 +180,30 @@ namespace ImageSharp.Formats
/// the data to image. /// the data to image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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="stream">The stream, where the image should be.</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param> /// <returns>The decoded image.</returns>
public void Decode<TColor>(Image<TColor> image, Stream stream, bool metadataOnly) public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
this.ProcessStream(image, stream, metadataOnly); ImageMetaData metadata = new ImageMetaData();
if (!metadataOnly) this.ProcessStream(metadata, stream, false);
{ this.ProcessBlocksIntoJpegImageChannels<TColor>();
this.ProcessBlocksIntoJpegImageChannels<TColor>(); Image<TColor> image = this.ConvertJpegPixelsToImagePixels<TColor>(metadata);
this.ConvertJpegPixelsToImagePixels(image);
} return image;
}
/// <summary>
/// Decodes the image from the specified <see cref="Stream"/> and sets
/// the data to image.
/// </summary>
/// <param name="stream">The stream, where the image should be.</param>
/// <returns>The image metadata.</returns>
public ImageMetaData DecodeMetaData(Stream stream)
{
ImageMetaData metadata = new ImageMetaData();
this.ProcessStream(metadata, stream, true);
return metadata;
} }
/// <summary> /// <summary>
@ -276,12 +288,10 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Read metadata from stream and read the blocks in the scans into <see cref="DecodedBlocks"/>. /// Read metadata from stream and read the blocks in the scans into <see cref="DecodedBlocks"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel type</typeparam> /// <param name="metadata">The metadata</param>
/// <param name="image">The <see cref="Image{TColor}"/></param>
/// <param name="stream">The stream</param> /// <param name="stream">The stream</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param> /// <param name="metadataOnly">Whether to decode metadata only.</param>
private void ProcessStream<TColor>(Image<TColor> image, Stream stream, bool metadataOnly) private void ProcessStream(ImageMetaData metadata, Stream stream, bool metadataOnly)
where TColor : struct, IPixel<TColor>
{ {
this.InputStream = stream; this.InputStream = stream;
this.InputProcessor = new InputProcessor(stream, this.Temp); this.InputProcessor = new InputProcessor(stream, this.Temp);
@ -429,7 +439,7 @@ namespace ImageSharp.Formats
this.ProcessApplicationHeader(remaining); this.ProcessApplicationHeader(remaining);
break; break;
case JpegConstants.Markers.APP1: case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining, image); this.ProcessApp1Marker(remaining, metadata);
break; break;
case JpegConstants.Markers.APP14: case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining); this.ProcessApp14Marker(remaining);
@ -496,13 +506,18 @@ namespace ImageSharp.Formats
/// Convert the pixel data in <see cref="YCbCrImage"/> and/or <see cref="JpegPixelArea"/> into pixels of <see cref="Image{TColor}"/> /// Convert the pixel data in <see cref="YCbCrImage"/> and/or <see cref="JpegPixelArea"/> into pixels of <see cref="Image{TColor}"/>
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel type</typeparam> /// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="image">The destination image</param> /// <param name="metadata">The metadata for the image.</param>
private void ConvertJpegPixelsToImagePixels<TColor>(Image<TColor> image) /// <returns>The decoded image.</returns>
private Image<TColor> ConvertJpegPixelsToImagePixels<TColor>(ImageMetaData metadata)
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
Image<TColor> image = new Image<TColor>(this.ImageWidth, this.ImageHeight);
image.MetaData.LoadFrom(metadata);
if (this.grayImage.IsInitialized) if (this.grayImage.IsInitialized)
{ {
this.ConvertFromGrayScale(this.ImageWidth, this.ImageHeight, image); this.ConvertFromGrayScale(image);
return image;
} }
else if (this.ycbcrImage != null) else if (this.ycbcrImage != null)
{ {
@ -519,27 +534,27 @@ namespace ImageSharp.Formats
// TODO: YCbCrA? // TODO: YCbCrA?
if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck) if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck)
{ {
this.ConvertFromYcck(this.ImageWidth, this.ImageHeight, image); this.ConvertFromYcck(image);
} }
else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown)
{ {
// Assume CMYK // Assume CMYK
this.ConvertFromCmyk(this.ImageWidth, this.ImageHeight, image); this.ConvertFromCmyk(image);
} }
return; return image;
} }
if (this.ComponentCount == 3) if (this.ComponentCount == 3)
{ {
if (this.IsRGB()) if (this.IsRGB())
{ {
this.ConvertFromRGB(this.ImageWidth, this.ImageHeight, image); this.ConvertFromRGB(image);
return; return image;
} }
this.ConvertFromYCbCr(this.ImageWidth, this.ImageHeight, image); this.ConvertFromYCbCr(image);
return; return image;
} }
throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces."); throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces.");
@ -582,28 +597,24 @@ namespace ImageSharp.Formats
/// Converts the image from the original CMYK image pixels. /// Converts the image from the original CMYK image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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> /// <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> where TColor : struct, IPixel<TColor>
{ {
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, image.Height,
y => y =>
{ {
// TODO: Simplify + optimize + share duplicate code across converter methods // TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y); int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(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 cyan = this.ycbcrImage.YChannel.Pixels[yo + x];
byte magenta = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; byte magenta = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -623,24 +634,20 @@ namespace ImageSharp.Formats
/// Converts the image from the original grayscale image pixels. /// Converts the image from the original grayscale image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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> /// <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> where TColor : struct, IPixel<TColor>
{ {
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, image.Height,
image.Configuration.ParallelOptions, image.Configuration.ParallelOptions,
y => y =>
{ {
int yoff = this.grayImage.GetRowOffset(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]; byte rgb = this.grayImage.Pixels[yoff + x];
@ -658,20 +665,17 @@ namespace ImageSharp.Formats
/// Converts the image from the original RBG image pixels. /// Converts the image from the original RBG image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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> /// <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> where TColor : struct, IPixel<TColor>
{ {
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, image.Height,
image.Configuration.ParallelOptions, image.Configuration.ParallelOptions,
y => y =>
{ {
@ -679,7 +683,7 @@ namespace ImageSharp.Formats
int yo = this.ycbcrImage.GetRowYOffset(y); int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(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 red = this.ycbcrImage.YChannel.Pixels[yo + x];
byte green = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; byte green = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -699,20 +703,16 @@ namespace ImageSharp.Formats
/// Converts the image from the original YCbCr image pixels. /// Converts the image from the original YCbCr image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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> /// <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> where TColor : struct, IPixel<TColor>
{ {
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, image.Height,
image.Configuration.ParallelOptions, image.Configuration.ParallelOptions,
y => y =>
{ {
@ -720,7 +720,7 @@ namespace ImageSharp.Formats
int yo = this.ycbcrImage.GetRowYOffset(y); int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(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 yy = this.ycbcrImage.YChannel.Pixels[yo + x];
byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -740,28 +740,24 @@ namespace ImageSharp.Formats
/// Converts the image from the original YCCK image pixels. /// Converts the image from the original YCCK image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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> /// <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> where TColor : struct, IPixel<TColor>
{ {
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, image.Height,
y => y =>
{ {
// TODO: Simplify + optimize + share duplicate code across converter methods // TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y); int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(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 yy = this.ycbcrImage.YChannel.Pixels[yo + x];
byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -959,11 +955,9 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Processes the App1 marker retrieving any stored metadata /// Processes the App1 marker retrieving any stored metadata
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
/// <param name="image">The image.</param> /// <param name="metadata">The image.</param>
private void ProcessApp1Marker<TColor>(int remaining, Image<TColor> image) private void ProcessApp1Marker(int remaining, ImageMetaData metadata)
where TColor : struct, IPixel<TColor>
{ {
if (remaining < 6 || this.options.IgnoreMetadata) if (remaining < 6 || this.options.IgnoreMetadata)
{ {
@ -978,7 +972,7 @@ namespace ImageSharp.Formats
&& profile[5] == '\0') && profile[5] == '\0')
{ {
this.isExif = true; this.isExif = true;
image.MetaData.ExifProfile = new ExifProfile(profile); metadata.ExifProfile = new ExifProfile(profile);
} }
} }

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

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

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

@ -148,7 +148,6 @@ namespace ImageSharp.Formats
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <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> /// <param name="stream">The stream containing image data. </param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk. /// Thrown if the stream does not contain and end chunk.
@ -156,10 +155,11 @@ namespace ImageSharp.Formats
/// <exception cref="System.ArgumentOutOfRangeException"> /// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size. /// Thrown if the image is larger than the maximum allowable size.
/// </exception> /// </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> where TColor : struct, IPixel<TColor>
{ {
Image<TColor> currentImage = image; ImageMetaData metadata = new ImageMetaData();
this.currentStream = stream; this.currentStream = stream;
this.currentStream.Skip(8); this.currentStream.Skip(8);
@ -177,7 +177,7 @@ namespace ImageSharp.Formats
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkTypes.Physical:
this.ReadPhysicalChunk(currentImage, currentChunk.Data); this.ReadPhysicalChunk(metadata, currentChunk.Data);
break; break;
case PngChunkTypes.Data: case PngChunkTypes.Data:
dataStream.Write(currentChunk.Data, 0, currentChunk.Length); dataStream.Write(currentChunk.Data, 0, currentChunk.Length);
@ -186,7 +186,7 @@ namespace ImageSharp.Formats
byte[] pal = new byte[currentChunk.Length]; byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length);
this.palette = pal; this.palette = pal;
image.MetaData.Quality = pal.Length / 3; metadata.Quality = pal.Length / 3;
break; break;
case PngChunkTypes.PaletteAlpha: case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length]; byte[] alpha = new byte[currentChunk.Length];
@ -194,7 +194,7 @@ namespace ImageSharp.Formats
this.paletteAlpha = alpha; this.paletteAlpha = alpha;
break; break;
case PngChunkTypes.Text: case PngChunkTypes.Text:
this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length); this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkTypes.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
@ -208,17 +208,20 @@ 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 = new Image<TColor>(this.header.Width, this.header.Height);
image.MetaData.LoadFrom(metadata);
using (PixelAccessor<TColor> pixels = image.Lock()) using (PixelAccessor<TColor> pixels = image.Lock())
{ {
this.ReadScanlines(dataStream, pixels); this.ReadScanlines(dataStream, pixels);
} }
return image;
} }
} }
@ -270,18 +273,16 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Reads the data chunk containing physical dimension data. /// Reads the data chunk containing physical dimension data.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="metadata">The metadata to read to.</param>
/// <param name="image">The image to read to.</param>
/// <param name="data">The data containing physical data.</param> /// <param name="data">The data containing physical data.</param>
private void ReadPhysicalChunk<TColor>(Image<TColor> image, byte[] data) private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data)
where TColor : struct, IPixel<TColor>
{ {
data.ReverseBytes(0, 4); data.ReverseBytes(0, 4);
data.ReverseBytes(4, 4); data.ReverseBytes(4, 4);
// 39.3700787 = inches in a meter. // 39.3700787 = inches in a meter.
image.MetaData.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d; metadata.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d;
image.MetaData.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d; metadata.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
} }
/// <summary> /// <summary>
@ -768,12 +769,10 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Reads a text chunk containing image properties from the data. /// Reads a text chunk containing image properties from the data.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="metadata">The metadata to decode to.</param>
/// <param name="image">The image to decode to.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param> /// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
/// <param name="length">The maximum length to read.</param> /// <param name="length">The maximum length to read.</param>
private void ReadTextChunk<TColor>(Image<TColor> image, byte[] data, int length) private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length)
where TColor : struct, IPixel<TColor>
{ {
if (this.options.IgnoreMetadata) if (this.options.IgnoreMetadata)
{ {
@ -794,7 +793,7 @@ namespace ImageSharp.Formats
string name = this.options.TextEncoding.GetString(data, 0, zeroIndex); string name = this.options.TextEncoding.GetString(data, 0, zeroIndex);
string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1); 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> /// <summary>

67
src/ImageSharp/Image.Decode.cs

@ -0,0 +1,67 @@
// <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;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Linq;
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>
/// 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>
/// <param name="img">The decoded image</param>
/// <returns>
/// [true] if can successfull decode the image otherwise [false].
/// </returns>
private static bool Decode<TColor>(Stream stream, IDecoderOptions options, Configuration config, out Image<TColor> img)
where TColor : struct, IPixel<TColor>
{
img = null;
int maxHeaderSize = config.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 = config.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header));
}
finally
{
ArrayPool<byte>.Shared.Return(header);
}
if (format == null)
{
return false;
}
img = format.Decoder.Decode<TColor>(stream, options);
img.CurrentImageFormat = format;
return true;
}
}
}

148
src/ImageSharp/Image.FromBytes.cs

@ -0,0 +1,148 @@
// <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.Buffers;
using System.Diagnostics;
using System.IO;
using System.Linq;
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(byte[] stream)
{
return Load(stream, null, 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(byte[] stream, IDecoderOptions options)
{
return Load(stream, options, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="config">The config 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[] stream, Configuration config)
{
return Load(stream, null, config);
}
/// <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>
/// <param name="config">The configuration options.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] stream, IDecoderOptions options, Configuration config)
{
using (MemoryStream ms = new MemoryStream(stream))
{
return Load(ms, options, config);
}
}
/// <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>(byte[] stream)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, null, 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>(byte[] stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, options, 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="config">The config 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[] stream, Configuration config)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, null, config);
}
/// <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>
/// <param name="config">The configuration options.</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[] stream, IDecoderOptions options, Configuration config)
where TColor : struct, IPixel<TColor>
{
using (MemoryStream ms = new MemoryStream(stream))
{
return Load<TColor>(ms, options, config);
}
}
}
}

148
src/ImageSharp/Image.FromFile.cs

@ -0,0 +1,148 @@
// <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.Buffers;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Formats;
#if !NETSTANDARD1_1
/// <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(string stream)
{
return Load(stream, null, 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(string stream, IDecoderOptions options)
{
return Load(stream, options, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="config">The config 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 stream, Configuration config)
{
return Load(stream, null, config);
}
/// <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>
/// <param name="config">The configuration options.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string stream, IDecoderOptions options, Configuration config)
{
return new Image(Image.Load<Color>(stream, options, config));
}
/// <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>(string stream)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, null, 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>(string stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, options, 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="config">The config 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 stream, Configuration config)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, null, config);
}
/// <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>
/// <param name="config">The configuration options.</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 stream, IDecoderOptions options, Configuration config)
where TColor : struct, IPixel<TColor>
{
config = config ?? Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(stream))
{
return Load<TColor>(s, options, config);
}
}
}
#endif
}

184
src/ImageSharp/Image.FromStream.cs

@ -0,0 +1,184 @@
// <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.Buffers;
using System.Diagnostics;
using System.IO;
using System.Linq;
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(stream, null, 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(stream, options, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="config">The config 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, Configuration config)
{
return Load(stream, null, config);
}
/// <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>
/// <param name="config">The configuration options.</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, Configuration config)
{
return new Image(Load<Color>(stream, options, config));
}
/// <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>(stream, null, 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>(stream, options, 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="config">The config 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, Configuration config)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, null, config);
}
/// <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>
/// <param name="config">The configuration options.</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, Configuration config)
where TColor : struct, IPixel<TColor>
{
config = config ?? Configuration.Default;
if (!config.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 (Decode(stream, options, config, out Image<TColor> img))
{
return img;
}
}
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 (Decode(ms, options, config, out Image<TColor> img))
{
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());
}
}
}

190
src/ImageSharp/Image.cs

@ -5,6 +5,7 @@
namespace ImageSharp namespace ImageSharp
{ {
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -15,7 +16,7 @@ namespace ImageSharp
/// packed into a single unsigned integer value. /// packed into a single unsigned integer value.
/// </summary> /// </summary>
[DebuggerDisplay("Image: {Width}x{Height}")] [DebuggerDisplay("Image: {Width}x{Height}")]
public sealed class Image : Image<Color> public sealed partial class Image : Image<Color>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Image"/> class /// Initializes a new instance of the <see cref="Image"/> class
@ -26,190 +27,19 @@ namespace ImageSharp
/// <param name="configuration"> /// <param name="configuration">
/// The configuration providing initialization code which allows extending the library. /// The configuration providing initialization code which allows extending the library.
/// </param> /// </param>
public Image(int width, int height, Configuration configuration = null) public Image(int width, int height, Configuration configuration)
: base(width, height, configuration) : base(width, height, configuration)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Image"/> class. /// Initializes a new instance of the <see cref="Image"/> class
/// </summary> /// with the height and the width of the image.
/// <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.
/// </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)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> 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)
: base(bytes, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary> /// </summary>
/// <param name="bytes"> /// <param name="width">The width of the image in pixels.</param>
/// The byte array containing image information. /// <param name="height">The height of the image in pixels.</param>
/// </param> public Image(int width, int height)
/// <param name="options"> : this(width, height, null)
/// 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(bytes, options, configuration)
{ {
} }
@ -219,7 +49,7 @@ namespace ImageSharp
/// </summary> /// </summary>
/// <param name="other">The other image, where the clone should be made from.</param> /// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception> /// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public Image(Image other) internal Image(Image<Color> other)
: base(other) : base(other)
{ {
} }

10
src/ImageSharp/Image/IImageBase.cs

@ -15,16 +15,6 @@ namespace ImageSharp
/// </summary> /// </summary>
Rectangle Bounds { get; } 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> /// <summary>
/// Gets the width in pixels. /// Gets the width in pixels.
/// </summary> /// </summary>

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

@ -21,16 +21,6 @@ namespace ImageSharp
/// </summary> /// </summary>
TColor[] Pixels { get; } 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> /// <summary>
/// Locks the image providing access to the pixels. /// Locks the image providing access to the pixels.
/// <remarks> /// <remarks>

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

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

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

@ -35,7 +35,7 @@ namespace ImageSharp
/// <param name="configuration"> /// <param name="configuration">
/// The configuration providing initialization code which allows extending the library. /// The configuration providing initialization code which allows extending the library.
/// </param> /// </param>
public Image(int width, int height, Configuration configuration = null) public Image(int width, int height, Configuration configuration)
: base(width, height, configuration) : base(width, height, configuration)
{ {
if (!this.Configuration.ImageFormats.Any()) if (!this.Configuration.ImageFormats.Any())
@ -43,203 +43,19 @@ namespace ImageSharp
throw new InvalidOperationException("No image formats have been configured."); throw new InvalidOperationException("No image formats have been configured.");
} }
this.MetaData = new ImageMetaData();
this.CurrentImageFormat = this.Configuration.ImageFormats.First(); this.CurrentImageFormat = this.Configuration.ImageFormats.First();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class. /// Initializes a new instance of the <see cref="Image{TColor}"/> class
/// </summary> /// with the height and the width of the image.
/// <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)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary> /// </summary>
/// <param name="bytes"> /// <param name="width">The width of the image in pixels.</param>
/// The byte array containing image information. /// <param name="height">The height of the image in pixels.</param>
/// </param> public Image(int width, int height)
/// <param name="options"> : this(width, height, null)
/// 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)
{ {
Guard.NotNull(bytes, nameof(bytes));
using (MemoryStream stream = new MemoryStream(bytes, false))
{
this.Load(stream, options);
}
} }
/// <summary> /// <summary>
@ -271,7 +87,6 @@ namespace ImageSharp
public Image(ImageBase<TColor> other) public Image(ImageBase<TColor> other)
: base(other) : base(other)
{ {
this.CopyProperties(other);
} }
/// <summary> /// <summary>
@ -588,103 +403,8 @@ namespace ImageSharp
/// </param> /// </param>
private void CopyProperties(IImage other) private void CopyProperties(IImage other)
{ {
base.CopyProperties(other);
this.CurrentImageFormat = other.CurrentImageFormat; this.CurrentImageFormat = other.CurrentImageFormat;
this.MetaData = new ImageMetaData(other.MetaData); 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;
}
} }
} }

44
src/ImageSharp/MetaData/ImageMetaData.cs

@ -5,6 +5,7 @@
namespace ImageSharp namespace ImageSharp
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
/// <summary> /// <summary>
@ -47,21 +48,7 @@ namespace ImageSharp
{ {
DebugGuard.NotNull(other, nameof(other)); DebugGuard.NotNull(other, nameof(other));
this.HorizontalResolution = other.HorizontalResolution; this.LoadFrom(other);
this.VerticalResolution = other.VerticalResolution;
this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay;
this.RepeatCount = other.RepeatCount;
foreach (ImageProperty property in other.Properties)
{
this.Properties.Add(new ImageProperty(property));
}
if (other.ExifProfile != null)
{
this.ExifProfile = new ExifProfile(other.ExifProfile);
}
} }
/// <summary> /// <summary>
@ -143,5 +130,32 @@ namespace ImageSharp
{ {
this.ExifProfile?.Sync(this); this.ExifProfile?.Sync(this);
} }
/// <summary>
/// Sets the current metadata values based on a previous metadata object.
/// </summary>
/// <param name="other">Meta data object to copy values from.</param>
internal void LoadFrom(ImageMetaData other)
{
this.HorizontalResolution = other.HorizontalResolution;
this.VerticalResolution = other.VerticalResolution;
this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay;
this.RepeatCount = other.RepeatCount;
foreach (ImageProperty property in other.Properties)
{
this.Properties.Add(new ImageProperty(property));
}
if (other.ExifProfile != null)
{
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)) using (MemoryStream memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength))
{ {
return new Image<TColor>(memStream); return Image.Load<TColor>(memStream);
} }
} }

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

@ -43,7 +43,7 @@ namespace ImageSharp.Benchmarks.Image
{ {
using (MemoryStream memoryStream = new MemoryStream(this.bmpBytes)) 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); 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) private Size LoadPng(MemoryStream stream)
{ {
using (Image image = new Image(stream)) using (Image image = Image.Load(stream))
{ {
return new Size(image.Width, image.Height); 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 (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); 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 (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); 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() public void DecodeJpegImageSharp()
{ {
this.ForEachStream( 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 (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); 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) if (this.bmpStream == null)
{ {
this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); 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.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream); 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) if (this.bmpStream == null)
{ {
this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); 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.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream); 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/Jpg/baseline/jpeg420exif.jpg"
: "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
this.bmpStream = File.OpenRead(path); this.bmpStream = File.OpenRead(path);
this.bmpCore = new Image(this.bmpStream); this.bmpCore = Image.Load(this.bmpStream);
this.bmpStream.Position = 0; this.bmpStream.Position = 0;
} }
} }

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

@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image
if (this.bmpStream == null) if (this.bmpStream == null)
{ {
this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); 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.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream); 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/Jpg/baseline/jpeg420exif.jpg"
: "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
this.bmpStream = File.OpenRead(path); this.bmpStream = File.OpenRead(path);
this.bmpCore = new CoreImage(this.bmpStream); this.bmpCore = CoreImage.Load(this.bmpStream);
this.bmpStream.Position = 0; this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream); 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)) 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")) using (FileStream stream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"))
{ {
this.image = new CoreImage(stream); this.image = CoreImage.Load(stream);
} }
} }
} }

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

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

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

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

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

@ -89,11 +89,10 @@ namespace ImageSharp.Tests
{ {
image.Save(ms, new JpegEncoder()); image.Save(ms, new JpegEncoder());
ms.Seek(0, SeekOrigin.Begin); 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))
{ {
decoder.Decode(mirror, ms, true); Image<TColor> mirror = decoder.Decode<TColor>(ms);
Assert.Equal(decoder.ImageWidth, image.Width); Assert.Equal(decoder.ImageWidth, image.Width);
Assert.Equal(decoder.ImageHeight, image.Height); 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); input.Save(memStream, new JpegFormat(), options);
memStream.Position = 0; memStream.Position = 0;
using (Image output = new Image(memStream)) using (Image output = Image.Load(memStream))
{ {
Assert.NotNull(output.MetaData.ExifProfile); Assert.NotNull(output.MetaData.ExifProfile);
} }
@ -113,7 +113,7 @@ namespace ImageSharp.Tests
input.SaveAsJpeg(memStream, options); input.SaveAsJpeg(memStream, options);
memStream.Position = 0; memStream.Position = 0;
using (Image output = new Image(memStream)) using (Image output = Image.Load(memStream))
{ {
Assert.Null(output.MetaData.ExifProfile); Assert.Null(output.MetaData.ExifProfile);
} }

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

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

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

@ -0,0 +1,341 @@
// <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<Color>(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<Stream>(), It.IsAny<IDecoderOptions>()))
.Callback<Stream, IDecoderOptions>((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);
}
[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);
}
[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);
}
[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);
}
[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);
}
[Fact]
public void LoadFromStreamWithConfig()
{
Stream stream = new MemoryStream();
Image img = Image.Load(stream, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(stream, null));
}
[Fact]
public void LoadFromStreamWithTypeAndConfig()
{
Stream stream = new MemoryStream();
Image<Color> img = Image.Load<Color>(stream, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(stream, null));
}
[Fact]
public void LoadFromStreamWithConfigAndOptions()
{
Stream stream = new MemoryStream();
Image img = Image.Load(stream, this.decoderOptions, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(stream, this.decoderOptions));
}
[Fact]
public void LoadFromStreamWithTypeAndConfigAndOptions()
{
Stream stream = new MemoryStream();
Image<Color> img = Image.Load<Color>(stream, this.decoderOptions, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(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);
}
[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);
}
[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);
}
[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);
}
[Fact]
public void LoadFromBytesWithConfig()
{
Image img = Image.Load(this.DataStream.ToArray(), this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(It.IsAny<Stream>(), null));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndConfig()
{
Image<Color> img = Image.Load<Color>(this.DataStream.ToArray(), this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(It.IsAny<Stream>(), null));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithConfigAndOptions()
{
Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(It.IsAny<Stream>(), this.decoderOptions));
Assert.Equal(this.DataStream.ToArray(), this.DecodedData);
}
[Fact]
public void LoadFromBytesWithTypeAndConfigAndOptions()
{
Image<Color> img = Image.Load<Color>(this.DataStream.ToArray(), this.decoderOptions, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(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);
}
[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);
}
[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);
}
[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);
}
[Fact]
public void LoadFromFileWithConfig()
{
Image img = Image.Load(this.FilePath, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.DataStream, null));
}
[Fact]
public void LoadFromFileWithTypeAndConfig()
{
Image<Color> img = Image.Load<Color>(this.FilePath, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.DataStream, null));
}
[Fact]
public void LoadFromFileWithConfigAndOptions()
{
Image img = Image.Load(this.FilePath, this.decoderOptions, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(this.DataStream, this.decoderOptions));
}
[Fact]
public void LoadFromFileWithTypeAndConfigAndOptions()
{
Image<Color> img = Image.Load<Color>(FilePath, this.decoderOptions, this.LocalConfiguration);
Assert.NotNull(img);
Assert.Equal(this.returnImage, img);
Assert.Equal(this.localFormat.Object, img.CurrentImageFormat);
this.localDecoder.Verify(x => x.Decode<Color>(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>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
new Image((byte[])null); Image.Load((byte[])null);
}); });
TestFile file = TestFile.Create(TestImages.Bmp.Car); 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(600, image.Width);
Assert.Equal(450, image.Height); Assert.Equal(450, image.Height);
@ -36,7 +36,7 @@ namespace ImageSharp.Tests
public void ConstructorFileSystem() public void ConstructorFileSystem()
{ {
TestFile file = TestFile.Create(TestImages.Bmp.Car); 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(600, image.Width);
Assert.Equal(450, image.Height); Assert.Equal(450, image.Height);
@ -49,7 +49,7 @@ namespace ImageSharp.Tests
System.IO.FileNotFoundException ex = Assert.Throws<System.IO.FileNotFoundException>( 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>( 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); image.SaveAsJpeg(memStream);
memStream.Position = 0; memStream.Position = 0;
image = new Image(memStream); image = Image.Load(memStream);
profile = image.MetaData.ExifProfile; profile = image.MetaData.ExifProfile;
Assert.NotNull(profile); Assert.NotNull(profile);
@ -91,7 +91,7 @@ namespace ImageSharp.Tests
image.SaveAsJpeg(memStream); image.SaveAsJpeg(memStream);
memStream.Position = 0; memStream.Position = 0;
image = new Image(memStream); image = Image.Load(memStream);
profile = image.MetaData.ExifProfile; profile = image.MetaData.ExifProfile;
Assert.NotNull(profile); Assert.NotNull(profile);
@ -286,7 +286,7 @@ namespace ImageSharp.Tests
image.Dispose(); image.Dispose();
memStream.Position = 0; 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.file = file;
this.Bytes = File.ReadAllBytes(file); this.Bytes = File.ReadAllBytes(file);
this.image = new Image(this.Bytes); this.image = Image.Load(this.Bytes);
} }
/// <summary> /// <summary>
@ -139,7 +139,7 @@ namespace ImageSharp.Tests
/// </returns> /// </returns>
public Image CreateImage(IDecoderOptions options) public Image CreateImage(IDecoderOptions options)
{ {
return new Image(this.Bytes, options); return Image.Load(this.Bytes, options);
} }
/// <summary> /// <summary>

174
tests/ImageSharp.Tests/TestFormat.cs

@ -0,0 +1,174 @@
// <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)
{
DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, options)).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;
public bool IsMatch(byte[] testMarker, IDecoderOptions testOptions)
{
if(this.options != testOptions)
{
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>(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
});
// 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) 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) 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 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); public override Image<Color> CreateImage(int width, int height) => new Image(width, height);

Loading…
Cancel
Save