Browse Source

Added an API to read base image information without decoding it

- intoduced base IImage interface
- introduced IImageInfoDetector interface
- Image.DetectPixelType method expanded to Identify method that returns IImage
pull/292/head
denisivan0v 8 years ago
parent
commit
a631a64dfb
  1. 11
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  2. 134
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 17
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  4. 182
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  5. 5
      src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
  6. 10
      src/ImageSharp/Formats/IImageDecoder.cs
  7. 21
      src/ImageSharp/Formats/IImageInfoDetector.cs
  8. 6
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
  9. 14
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  10. 6
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  11. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs
  12. 2
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  13. 6
      src/ImageSharp/Formats/PixelTypeInfo.cs
  14. 15
      src/ImageSharp/Formats/Png/PngDecoder.cs
  15. 69
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  16. 31
      src/ImageSharp/Image/IImage.cs
  17. 10
      src/ImageSharp/Image/Image.Decode.cs
  18. 16
      src/ImageSharp/Image/Image.FromStream.cs
  19. 4
      src/ImageSharp/Image/ImageFrameCollection.cs
  20. 24
      src/ImageSharp/Image/ImageInfo.cs
  21. 31
      src/ImageSharp/Image/Image{TPixel}.cs
  22. 2
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  23. 2
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  24. 2
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  25. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  26. 2
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  27. 5
      tests/ImageSharp.Tests/TestFormat.cs
  28. 21
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
  29. 10
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

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

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
@ -23,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
/// </remarks>
public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions
public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
@ -36,14 +34,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
public IImage Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
byte[] buffer = new byte[2];
stream.Skip(28);
stream.Read(buffer, 0, 2);
return new PixelTypeInfo(BitConverter.ToInt16(buffer, 0));
return new BmpDecoderCore(configuration, this).Identify(stream);
}
}
}

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

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@ -94,62 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.currentStream = stream;
try
{
this.ReadFileHeader();
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
// is the upper-left corner and not the lower-left.The inverted flag
// indicates a lower-left origin.Our code will be outputting an
// upper-left origin pixel array.
bool inverted = false;
if (this.infoHeader.Height < 0)
{
inverted = true;
this.infoHeader.Height = -this.infoHeader.Height;
}
int colorMapSize = -1;
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
}
byte[] palette = null;
if (colorMapSize > 0)
{
// 256 * 4
if (colorMapSize > 1024)
{
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TPixel> pixels = image.Lock())
@ -192,6 +140,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
/// <summary>
/// Reads the image base information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImage Identify(Stream stream)
{
this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData());
}
/// <summary>
/// Returns the y- value based on the given height.
/// </summary>
@ -624,5 +582,73 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Offset = BitConverter.ToInt32(data, 10)
};
}
/// <summary>
/// Reads the <see cref="BmpFileHeader"/> and <see cref="BmpInfoHeader"/> from the stream and sets the corresponding fields.
/// </summary>
private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
this.currentStream = stream;
try
{
this.ReadFileHeader();
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
// is the upper-left corner and not the lower-left.The inverted flag
// indicates a lower-left origin.Our code will be outputting an
// upper-left origin pixel array.
inverted = false;
if (this.infoHeader.Height < 0)
{
inverted = true;
this.infoHeader.Height = -this.infoHeader.Height;
}
int colorMapSize = -1;
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
}
palette = null;
if (colorMapSize > 0)
{
// 256 * 4
if (colorMapSize > 1024)
{
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
}
catch (IndexOutOfRangeException e)
{
throw new ImageFormatException("Bitmap does not have a valid format.", e);
}
}
}
}

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

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.PixelFormats;
@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Decoder for generating an image out of a gif encoded stream.
/// </summary>
public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions
public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -33,20 +31,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var decoder = new GifDecoderCore<TPixel>(configuration, this);
return decoder.Decode(stream);
var decoder = new GifDecoderCore(configuration, this);
return decoder.Decode<TPixel>(stream);
}
/// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
public IImage Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
byte[] buffer = new byte[1];
stream.Skip(10); // Skip the identifier and size
stream.Read(buffer, 0, 1); // Skip the identifier and size
int bitsPerPixel = (buffer[0] & 0x07) + 1; // The lowest 3 bits represent the bit depth minus 1
return new PixelTypeInfo(bitsPerPixel);
var decoder = new GifDecoderCore(configuration, this);
return decoder.Identify(stream);
}
}
}

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

@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Performs the gif decoding operation.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal sealed class GifDecoderCore<TPixel>
where TPixel : struct, IPixel<TPixel>
internal sealed class GifDecoderCore
{
/// <summary>
/// The temp buffer used to reduce allocations.
@ -46,11 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private int globalColorTableLength;
/// <summary>
/// The previous frame.
/// </summary>
private ImageFrame<TPixel> previousFrame;
/// <summary>
/// The area to restore.
/// </summary>
@ -72,12 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private ImageMetaData metaData;
/// <summary>
/// The image to decode the information to.
/// </summary>
private Image<TPixel> image;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The decoder options.</param>
@ -107,28 +95,84 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data. </param>
/// <returns>The decoded image</returns>
public Image<TPixel> Decode(Stream stream)
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel>
{
Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null;
try
{
this.metaData = new ImageMetaData();
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
this.currentStream = stream;
// Loop though the respective gif parts and read the data.
int nextFlag = stream.ReadByte();
while (nextFlag != GifConstants.Terminator)
{
if (nextFlag == GifConstants.ImageLabel)
{
if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First)
{
break;
}
// Skip the identifier
this.currentStream.Skip(6);
this.ReadLogicalScreenDescriptor();
this.ReadFrame(ref image, ref previousFrame);
}
else if (nextFlag == GifConstants.ExtensionIntroducer)
{
int label = stream.ReadByte();
switch (label)
{
case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
break;
case GifConstants.CommentLabel:
this.ReadComments();
break;
case GifConstants.ApplicationExtensionLabel:
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = Buffer<byte>.CreateClean(this.globalColorTableLength);
// The application extension length should be 11 but we've got test images that incorrectly
// set this to 252.
int appLength = stream.ReadByte();
this.Skip(appLength); // No need to read.
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
break;
}
}
else if (nextFlag == GifConstants.EndIntroducer)
{
break;
}
// Read the global color table from the stream
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
nextFlag = stream.ReadByte();
if (nextFlag == -1)
{
break;
}
}
}
finally
{
this.globalColorTable?.Dispose();
}
return image;
}
/// <summary>
/// Reads the image base information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImage Identify(Stream stream)
{
try
{
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
// Loop though the respective gif parts and read the data.
int nextFlag = stream.ReadByte();
@ -136,12 +180,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
if (nextFlag == GifConstants.ImageLabel)
{
if (this.previousFrame != null && this.DecodingMode == FrameDecodingMode.First)
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
if (imageDescriptor.LocalColorTableFlag)
{
break;
int length = imageDescriptor.LocalColorTableSize * 3;
// Skip local color table block
this.Skip(length);
}
this.ReadFrame();
// Skip image block
this.Skip(0);
}
else if (nextFlag == GifConstants.ExtensionIntroducer)
{
@ -149,7 +200,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (label)
{
case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
// Skip graphic control extension block
this.Skip(0);
break;
case GifConstants.CommentLabel:
this.ReadComments();
@ -184,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.globalColorTable?.Dispose();
}
return this.image;
return new ImageInfo(new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, this.metaData);
}
/// <summary>
@ -242,6 +295,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
Width = BitConverter.ToInt16(this.buffer, 0),
Height = BitConverter.ToInt16(this.buffer, 2),
BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1
BackgroundColorIndex = this.buffer[5],
PixelAspectRatio = this.buffer[6],
GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
@ -308,7 +362,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reads an individual gif frame.
/// </summary>
private void ReadFrame()
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</param>
private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame)
where TPixel : struct, IPixel<TPixel>
{
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
@ -327,7 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
indices = Buffer<byte>.CreateClean(imageDescriptor.Width * imageDescriptor.Height);
this.ReadFrameIndices(imageDescriptor, indices);
this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor);
this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor);
// Skip any remaining blocks
this.Skip(0);
@ -357,10 +415,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reads the frames colors, mapping indices to colors.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</param>
/// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors(Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor)
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor)
where TPixel : struct, IPixel<TPixel>
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
@ -371,30 +433,30 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageFrame<TPixel> imageFrame;
if (this.previousFrame == null)
if (previousFrame == null)
{
// This initializes the image to become fully transparent because the alpha channel is zero.
this.image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, this.metaData);
image = new Image<TPixel>(this.configuration, new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), imageWidth, imageHeight, this.metaData);
this.SetFrameMetaData(this.image.Frames.RootFrame.MetaData);
this.SetFrameMetaData(image.Frames.RootFrame.MetaData);
imageFrame = this.image.Frames.RootFrame;
imageFrame = image.Frames.RootFrame;
}
else
{
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
prevFrame = this.previousFrame;
prevFrame = previousFrame;
}
currentFrame = this.image.Frames.AddFrame(this.previousFrame); // This clones the frame and adds it the collection
currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection
this.SetFrameMetaData(currentFrame.MetaData);
imageFrame = currentFrame;
this.RestoreToBackground(imageFrame);
this.RestoreToBackground(imageFrame, image.Width, image.Height);
}
int i = 0;
@ -466,11 +528,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (prevFrame != null)
{
this.previousFrame = prevFrame;
previousFrame = prevFrame;
return;
}
this.previousFrame = currentFrame ?? this.image.Frames.RootFrame;
previousFrame = currentFrame ?? image.Frames.RootFrame;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -482,8 +544,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Restores the current frame area to the background.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The frame.</param>
private void RestoreToBackground(ImageFrame<TPixel> frame)
/// <param name="imageWidth">Width of the image.</param>
/// <param name="imageHeight">Height of the image.</param>
private void RestoreToBackground<TPixel>(ImageFrame<TPixel> frame, int imageWidth, int imageHeight)
where TPixel : struct, IPixel<TPixel>
{
if (this.restoreArea == null)
{
@ -491,8 +557,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
// Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.image.Width &&
this.restoreArea.Value.Height == this.image.Height)
if (this.restoreArea.Value.Width == imageWidth &&
this.restoreArea.Value.Height == imageHeight)
{
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{
@ -533,5 +599,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
}
}
/// <summary>
/// Reads the logical screen descriptor and global color table blocks
/// </summary>
/// <param name="stream">The stream containing image data. </param>
private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
{
this.metaData = new ImageMetaData();
this.currentStream = stream;
// Skip the identifier
this.currentStream.Skip(6);
this.ReadLogicalScreenDescriptor();
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = Buffer<byte>.CreateClean(this.globalColorTableLength);
// Read the global color table from the stream
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
}
}
}
}

5
src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs

@ -21,6 +21,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// rendered in the displaying device.
/// </summary>
public short Height { get; set; }
/// <summary>
/// Gets or sets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel { get; set; }
/// <summary>
/// Gets or sets the index at the Global Color Table for the Background Color.

10
src/ImageSharp/Formats/IImageDecoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
@ -22,13 +20,5 @@ namespace SixLabors.ImageSharp.Formats
/// <returns>The decoded image</returns>
Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Detects the image pixel size from the specified stream.
/// </summary>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="PixelTypeInfo"/> object</returns>
PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream);
}
}

21
src/ImageSharp/Formats/IImageInfoDetector.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Used for detecting the image base information without decoding it.
/// </summary>
public interface IImageInfoDetector
{
/// <summary>
/// Reads the image base information from the specified stream.
/// </summary>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="PixelTypeInfo"/> object</returns>
IImage Identify(Configuration configuration, Stream stream);
}
}

6
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary>
/// Image decoder for generating an image out of a jpg stream.
/// </summary>
internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions
internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
/// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
public IImage Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
using (var decoder = new OrigJpegDecoderCore(configuration, this))
{
return new PixelTypeInfo(decoder.DetectPixelSize(stream));
return decoder.Identify(stream);
}
}
}

14
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -127,6 +127,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components;
/// <summary>
/// Gets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
/// <summary>
/// Gets the image height
/// </summary>
@ -193,14 +198,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
/// <summary>
/// Detects the image pixel size from the specified stream.
/// Reads the image base information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The color depth, in number of bits per pixel</returns>
public int DetectPixelSize(Stream stream)
public IImage Identify(Stream stream)
{
this.ParseStream(stream, true);
return this.ComponentCount * SupportedPrecision;
return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
}
/// <inheritdoc />
@ -785,7 +789,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
using (var postProcessor = new JpegImagePostProcessor(this))
{
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
var image = new Image<TPixel>(this.configuration, new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);
return image;
}

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

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Image decoder for generating an image out of a jpg stream.
/// </summary>
public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions
public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -31,13 +31,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
/// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
public IImage Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
using (var decoder = new OrigJpegDecoderCore(configuration, this))
{
return new PixelTypeInfo(decoder.DetectPixelSize(stream));
return decoder.Identify(stream);
}
}
}

6
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs

@ -27,11 +27,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return decoder.Decode<TPixel>(stream);
}
}
/// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
{
throw new System.NotImplementedException();
}
}
}

2
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.QuantizeAndInverseAllComponents();
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, metadata);
var image = new Image<TPixel>(this.configuration, null, this.ImageWidth, this.ImageHeight, metadata);
this.FillPixelData(image.Frames.RootFrame);
this.AssignResolution(image);
return image;

6
src/ImageSharp/Formats/PixelTypeInfo.cs

@ -1,21 +1,21 @@
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Stores information about pixel
/// Stores information about pixel.
/// </summary>
public class PixelTypeInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelTypeInfo"/> class.
/// </summary>
/// <param name="bitsPerPixel">color depth, in number of bits per pixel</param>
/// <param name="bitsPerPixel">Color depth, in number of bits per pixel.</param>
internal PixelTypeInfo(int bitsPerPixel)
{
this.BitsPerPixel = bitsPerPixel;
}
/// <summary>
/// Gets color depth, in number of bits per pixel
/// Gets color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel { get; }
}

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

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.PixelFormats;
@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </list>
/// </para>
/// </remarks>
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -55,16 +53,11 @@ namespace SixLabors.ImageSharp.Formats.Png
return decoder.Decode<TPixel>(stream);
}
/// <summary>
/// Detects the image pixel size from the specified stream.
/// </summary>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The color depth, in number of bits per pixel</returns>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
/// <inheritdoc/>
public IImage Identify(Configuration configuration, Stream stream)
{
var decoder = new PngDecoderCore(configuration, this);
return new PixelTypeInfo(decoder.DetectPixelSize(stream));
return decoder.Identify(stream);
}
}
}

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

@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Png
deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
stream.Read(this.crcBuffer, 0, 4);
this.currentStream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length];
@ -279,42 +279,50 @@ namespace SixLabors.ImageSharp.Formats.Png
}
/// <summary>
/// Detects the image pixel size from the specified stream.
/// Reads the image base information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The color depth, in number of bits per pixel</returns>
public int DetectPixelSize(Stream stream)
public IImage Identify(Stream stream)
{
var metadata = new ImageMetaData();
this.currentStream = stream;
this.currentStream.Skip(8);
try
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{
try
{
try
switch (currentChunk.Type)
{
switch (currentChunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data);
this.ValidateHeader();
this.isEndChunkReached = true;
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
break;
}
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data);
break;
case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
break;
}
finally
}
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
{
ArrayPool<byte>.Shared.Return(currentChunk.Data);
}
ArrayPool<byte>.Shared.Return(currentChunk.Data);
}
}
}
}
finally
{
@ -327,7 +335,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("PNG Image hasn't header chunk");
}
return this.CalculateBitsPerPixel();
return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
}
/// <summary>
@ -418,7 +426,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);
image = new Image<TPixel>(this.configuration, new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 1;
@ -1255,6 +1263,15 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
/// <summary>
/// Skips the chunk data and the cycle redundancy chunk read from the data.
/// </summary>
private void SkipChunkDataAndCrc(PngChunk chunk)
{
this.currentStream.Skip(chunk.Length);
this.currentStream.Skip(4);
}
/// <summary>
/// Reads the chunk data from the stream.
/// </summary>

31
src/ImageSharp/Image/IImage.cs

@ -0,0 +1,31 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Represents the base image abstraction.
/// </summary>
public interface IImage
{
/// <summary>
/// Gets information about pixel.
/// </summary>
PixelTypeInfo PixelType { get; }
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the meta data of the image.
/// </summary>
ImageMetaData MetaData { get; }
}
}

10
src/ImageSharp/Image/Image.Decode.cs

@ -81,17 +81,17 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Detects the image pixel size.
/// Reads the image base information.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="config">the configuration.</param>
/// <returns>
/// The <see cref="PixelTypeInfo"/> or null if suitable decoder not found.
/// The <see cref="IImage"/> or null if suitable info detector not found.
/// </returns>
private static PixelTypeInfo InternalDetectPixelType(Stream stream, Configuration config)
private static IImage InternalIdentity(Stream stream, Configuration config)
{
IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat _);
return decoder?.DetectPixelType(config, stream);
var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector;
return detector?.Identify(config, stream);
}
}
}

16
src/ImageSharp/Image/Image.FromStream.cs

@ -37,22 +37,22 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// By reading the header on the provided stream this calculates the images color depth.
/// By reading the header on the provided stream this reads the image base information.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>
/// The <see cref="PixelTypeInfo"/> or null if suitable decoder not found.
/// The <see cref="IImage"/> or null if suitable info detector not found.
/// </returns>
public static PixelTypeInfo DetectPixelType(Stream stream)
public static IImage Identify(Stream stream)
{
return DetectPixelType(null, stream);
return Identify(null, stream);
}
/// <summary>
/// By reading the header on the provided stream this calculates the images color depth.
/// By reading the header on the provided stream this reads the image base information.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="stream">The image stream to read the header from.</param>
@ -60,11 +60,11 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>
/// The <see cref="PixelTypeInfo"/> or null if suitable decoder not found.
/// The <see cref="IImage"/> or null if suitable info detector not found.
/// </returns>
public static PixelTypeInfo DetectPixelType(Configuration config, Stream stream)
public static IImage Identify(Configuration config, Stream stream)
{
return WithSeekableStream(stream, s => InternalDetectPixelType(s, config ?? Configuration.Default));
return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default));
}
/// <summary>

4
src/ImageSharp/Image/ImageFrameCollection.cs

@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp
this.frames.Remove(frame);
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame });
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.PixelType, this.parent.MetaData.Clone(), new[] { frame });
}
/// <inheritdoc/>
@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp
{
ImageFrame<TPixel> frame = this[index];
ImageFrame<TPixel> clonedFrame = frame.Clone();
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame });
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.PixelType, this.parent.MetaData.Clone(), new[] { clonedFrame });
}
/// <inheritdoc/>

24
src/ImageSharp/Image/ImageInfo.cs

@ -0,0 +1,24 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
internal sealed class ImageInfo : IImage
{
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData)
{
this.PixelType = pixelType;
this.Width = width;
this.Height = height;
this.MetaData = metaData;
}
public PixelTypeInfo PixelType { get; }
public int Width { get; }
public int Height { get; }
public ImageMetaData MetaData { get; }
}
}

31
src/ImageSharp/Image/Image{TPixel}.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed partial class Image<TPixel> : IDisposable, IConfigurable
public sealed partial class Image<TPixel> : IImage, IDisposable, IConfigurable
where TPixel : struct, IPixel<TPixel>
{
private Configuration configuration;
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public Image(Configuration configuration, int width, int height)
: this(configuration, width, height, new ImageMetaData())
: this(configuration, null, width, height, new ImageMetaData())
{
}
@ -55,12 +55,14 @@ namespace SixLabors.ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <param name="pixelType">The information about pixel of the image.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata)
internal Image(Configuration configuration, PixelTypeInfo pixelType, int width, int height, ImageMetaData metadata)
{
this.configuration = configuration ?? Configuration.Default;
this.PixelType = pixelType;
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, width, height);
}
@ -70,11 +72,13 @@ namespace SixLabors.ImageSharp
/// with the height and the width of the image.
/// </summary>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <param name="pixelType">The information about pixel of the image.</param>
/// <param name="metadata">The images metadata.</param>
/// <param name="frames">The frames that will be owned by this image instance.</param>
internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames)
internal Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames)
{
this.configuration = configuration ?? Configuration.Default;
this.PixelType = pixelType;
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, frames);
@ -84,20 +88,17 @@ namespace SixLabors.ImageSharp
/// Gets the pixel buffer.
/// </summary>
Configuration IConfigurable.Configuration => this.configuration;
/// <inheritdoc/>
public PixelTypeInfo PixelType { get; }
/// <summary>
/// Gets the width.
/// </summary>
/// <inheritdoc/>
public int Width => this.frames.RootFrame.Width;
/// <summary>
/// Gets the height.
/// </summary>
/// <inheritdoc/>
public int Height => this.frames.RootFrame.Height;
/// <summary>
/// Gets the meta data of the image.
/// </summary>
/// <inheritdoc/>
public ImageMetaData MetaData { get; private set; } = new ImageMetaData();
/// <summary>
@ -144,7 +145,7 @@ namespace SixLabors.ImageSharp
public Image<TPixel> Clone()
{
IEnumerable<ImageFrame<TPixel>> clonedFrames = this.frames.Select(x => x.Clone());
return new Image<TPixel>(this.configuration, this.MetaData.Clone(), clonedFrames);
return new Image<TPixel>(this.configuration, this.PixelType, this.MetaData.Clone(), clonedFrames);
}
/// <inheritdoc/>
@ -162,7 +163,7 @@ namespace SixLabors.ImageSharp
where TPixel2 : struct, IPixel<TPixel2>
{
IEnumerable<ImageFrame<TPixel2>> clonedFrames = this.frames.Select(x => x.CloneAs<TPixel2>());
var target = new Image<TPixel2>(this.configuration, this.MetaData.Clone(), clonedFrames);
var target = new Image<TPixel2>(this.configuration, this.PixelType, this.MetaData.Clone(), clonedFrames);
return target;
}

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

@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
// For resize we know we are going to populate every pixel with fresh data and we want a different target size so
// let's manually clone an empty set of images at the correct target and then have the base class process them in turn.
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders
var image = new Image<TPixel>(config, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added
var image = new Image<TPixel>(config, source.PixelType, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added
return image;
}

2
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel);
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
}
}
}

2
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Tests
TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel);
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
}
}

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

@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel);
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
}
}
}

2
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Tests
TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel);
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
}
}

5
tests/ImageSharp.Tests/TestFormat.cs

@ -201,11 +201,6 @@ namespace SixLabors.ImageSharp.Tests
return this.testFormat.Sample<TPixel>();
}
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
{
throw new NotImplementedException();
}
public bool IsSupportedFileFormat(Span<byte> header) => testFormat.IsSupportedFileFormat(header);
}

21
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs

@ -1,19 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
public class SystemDrawingReferenceDecoder : IImageDecoder
using SixLabors.ImageSharp.MetaData;
public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector
{
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder();
@ -22,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
using (var sourceBitmap = new System.Drawing.Bitmap(stream))
{
if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb)
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap<TPixel>(sourceBitmap);
}
@ -32,12 +30,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
sourceBitmap.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var g = Graphics.FromImage(convertedBitmap))
using (var g = System.Drawing.Graphics.FromImage(convertedBitmap))
{
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
}
@ -46,11 +44,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
}
}
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
public IImage Identify(Configuration configuration, Stream stream)
{
using (var sourceBitmap = new System.Drawing.Bitmap(stream))
{
return new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat));
var pixelType = new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat));
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetaData());
}
}
}

10
tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

@ -83,11 +83,6 @@ namespace SixLabors.ImageSharp.Tests
return new Image<TPixel>(42, 42);
}
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
{
throw new NotImplementedException();
}
// Couldn't make xUnit happy without this hackery:
private static ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>();
@ -150,11 +145,6 @@ namespace SixLabors.ImageSharp.Tests
return new Image<TPixel>(42, 42);
}
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
{
throw new NotImplementedException();
}
private static ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>();
private string callerName = null;

Loading…
Cancel
Save