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

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

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp namespace SixLabors.ImageSharp.Formats.Bmp
@ -94,62 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
this.currentStream = stream;
try try
{ {
this.ReadFileHeader(); this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
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}'");
}
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height); var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TPixel> pixels = image.Lock()) 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> /// <summary>
/// Returns the y- value based on the given height. /// Returns the y- value based on the given height.
/// </summary> /// </summary>
@ -624,5 +582,73 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Offset = BitConverter.ToInt32(data, 10) 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Decoder for generating an image out of a gif encoded stream. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </summary>
public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// 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) public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var decoder = new GifDecoderCore<TPixel>(configuration, this); var decoder = new GifDecoderCore(configuration, this);
return decoder.Decode(stream); return decoder.Decode<TPixel>(stream);
} }
/// <inheritdoc/> /// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) public IImage Identify(Configuration configuration, Stream stream)
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, "stream");
byte[] buffer = new byte[1]; var decoder = new GifDecoderCore(configuration, this);
stream.Skip(10); // Skip the identifier and size return decoder.Identify(stream);
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);
} }
} }
} }

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

@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> internal sealed class GifDecoderCore
internal sealed class GifDecoderCore<TPixel>
where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// The temp buffer used to reduce allocations. /// The temp buffer used to reduce allocations.
@ -46,11 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary> /// </summary>
private int globalColorTableLength; private int globalColorTableLength;
/// <summary>
/// The previous frame.
/// </summary>
private ImageFrame<TPixel> previousFrame;
/// <summary> /// <summary>
/// The area to restore. /// The area to restore.
/// </summary> /// </summary>
@ -72,12 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private ImageMetaData metaData; private ImageMetaData metaData;
/// <summary> /// <summary>
/// The image to decode the information to. /// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary>
private Image<TPixel> image;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
@ -107,28 +95,84 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data. </param> /// <param name="stream">The stream containing image data. </param>
/// <returns>The decoded image</returns> /// <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 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.ReadFrame(ref image, ref previousFrame);
this.currentStream.Skip(6); }
this.ReadLogicalScreenDescriptor(); 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) // The application extension length should be 11 but we've got test images that incorrectly
{ // set this to 252.
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; int appLength = stream.ReadByte();
this.globalColorTable = Buffer<byte>.CreateClean(this.globalColorTableLength); 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 nextFlag = stream.ReadByte();
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); 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. // Loop though the respective gif parts and read the data.
int nextFlag = stream.ReadByte(); int nextFlag = stream.ReadByte();
@ -136,12 +180,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
if (nextFlag == GifConstants.ImageLabel) 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) else if (nextFlag == GifConstants.ExtensionIntroducer)
{ {
@ -149,7 +200,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (label) switch (label)
{ {
case GifConstants.GraphicControlLabel: case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
// Skip graphic control extension block
this.Skip(0);
break; break;
case GifConstants.CommentLabel: case GifConstants.CommentLabel:
this.ReadComments(); this.ReadComments();
@ -184,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.globalColorTable?.Dispose(); this.globalColorTable?.Dispose();
} }
return this.image; return new ImageInfo(new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, this.metaData);
} }
/// <summary> /// <summary>
@ -242,6 +295,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
Width = BitConverter.ToInt16(this.buffer, 0), Width = BitConverter.ToInt16(this.buffer, 0),
Height = BitConverter.ToInt16(this.buffer, 2), 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], BackgroundColorIndex = this.buffer[5],
PixelAspectRatio = this.buffer[6], PixelAspectRatio = this.buffer[6],
GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1, GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
@ -308,7 +362,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Reads an individual gif frame. /// Reads an individual gif frame.
/// </summary> /// </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(); GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
@ -327,7 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
indices = Buffer<byte>.CreateClean(imageDescriptor.Width * imageDescriptor.Height); indices = Buffer<byte>.CreateClean(imageDescriptor.Width * imageDescriptor.Height);
this.ReadFrameIndices(imageDescriptor, indices); 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 // Skip any remaining blocks
this.Skip(0); this.Skip(0);
@ -357,10 +415,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Reads the frames colors, mapping indices to colors. /// Reads the frames colors, mapping indices to colors.
/// </summary> /// </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="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></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 imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height; int imageHeight = this.logicalScreenDescriptor.Height;
@ -371,30 +433,30 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageFrame<TPixel> imageFrame; 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 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 else
{ {
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) 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); this.SetFrameMetaData(currentFrame.MetaData);
imageFrame = currentFrame; imageFrame = currentFrame;
this.RestoreToBackground(imageFrame); this.RestoreToBackground(imageFrame, image.Width, image.Height);
} }
int i = 0; int i = 0;
@ -466,11 +528,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (prevFrame != null) if (prevFrame != null)
{ {
this.previousFrame = prevFrame; previousFrame = prevFrame;
return; return;
} }
this.previousFrame = currentFrame ?? this.image.Frames.RootFrame; previousFrame = currentFrame ?? image.Frames.RootFrame;
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -482,8 +544,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Restores the current frame area to the background. /// Restores the current frame area to the background.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The frame.</param> /// <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) 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. // Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.image.Width && if (this.restoreArea.Value.Width == imageWidth &&
this.restoreArea.Value.Height == this.image.Height) this.restoreArea.Value.Height == imageHeight)
{ {
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock()) using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{ {
@ -533,5 +599,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod; 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. /// rendered in the displaying device.
/// </summary> /// </summary>
public short Height { get; set; } 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> /// <summary>
/// Gets or sets the index at the Global Color Table for the Background Color. /// 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -22,13 +20,5 @@ namespace SixLabors.ImageSharp.Formats
/// <returns>The decoded image</returns> /// <returns>The decoded image</returns>
Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream) Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>; 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> /// <summary>
/// Image decoder for generating an image out of a jpg stream. /// Image decoder for generating an image out of a jpg stream.
/// </summary> /// </summary>
internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// 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/> /// <inheritdoc/>
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) public IImage Identify(Configuration configuration, Stream stream)
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, "stream");
using (var decoder = new OrigJpegDecoderCore(configuration, this)) 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; 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> /// <summary>
/// Gets the image height /// Gets the image height
/// </summary> /// </summary>
@ -193,14 +198,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
/// <summary> /// <summary>
/// Detects the image pixel size from the specified stream. /// Reads the image base information from the specified stream.
/// </summary> /// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The color depth, in number of bits per pixel</returns> public IImage Identify(Stream stream)
public int DetectPixelSize(Stream stream)
{ {
this.ParseStream(stream, true); this.ParseStream(stream, true);
return this.ComponentCount * SupportedPrecision; return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -785,7 +789,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
using (var postProcessor = new JpegImagePostProcessor(this)) 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); postProcessor.PostProcess(image.Frames.RootFrame);
return image; return image;
} }

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

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

6
src/ImageSharp/Formats/PixelTypeInfo.cs

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

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

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </list> /// </list>
/// </para> /// </para>
/// </remarks> /// </remarks>
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// 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); return decoder.Decode<TPixel>(stream);
} }
/// <summary> /// <inheritdoc/>
/// Detects the image pixel size from the specified stream. public IImage Identify(Configuration configuration, Stream 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)
{ {
var decoder = new PngDecoderCore(configuration, this); 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); deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
stream.Read(this.crcBuffer, 0, 4); this.currentStream.Read(this.crcBuffer, 0, 4);
break; break;
case PngChunkTypes.Palette: case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length]; byte[] pal = new byte[currentChunk.Length];
@ -279,42 +279,50 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
/// <summary> /// <summary>
/// Detects the image pixel size from the specified stream. /// Reads the image base information from the specified stream.
/// </summary> /// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The color depth, in number of bits per pixel</returns> public IImage Identify(Stream stream)
public int DetectPixelSize(Stream stream)
{ {
var metadata = new ImageMetaData();
this.currentStream = stream; this.currentStream = stream;
this.currentStream.Skip(8); this.currentStream.Skip(8);
try try
{ {
PngChunk currentChunk; PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{
try
{ {
try switch (currentChunk.Type)
{ {
switch (currentChunk.Type) case PngChunkTypes.Header:
{ this.ReadHeaderChunk(currentChunk.Data);
case PngChunkTypes.Header: this.ValidateHeader();
this.ReadHeaderChunk(currentChunk.Data); break;
this.ValidateHeader(); case PngChunkTypes.Physical:
this.isEndChunkReached = true; this.ReadPhysicalChunk(metadata, currentChunk.Data);
break; break;
case PngChunkTypes.End: case PngChunkTypes.Data:
this.isEndChunkReached = true; this.SkipChunkDataAndCrc(currentChunk);
break; 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() ArrayPool<byte>.Shared.Return(currentChunk.Data);
if (currentChunk.Data != null)
{
ArrayPool<byte>.Shared.Return(currentChunk.Data);
}
} }
} }
}
} }
finally finally
{ {
@ -327,7 +335,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("PNG Image hasn't header chunk"); 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> /// <summary>
@ -418,7 +426,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image) private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image)
where TPixel : struct, IPixel<TPixel> 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.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 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> /// <summary>
/// Reads the chunk data from the stream. /// Reads the chunk data from the stream.
/// </summary> /// </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> /// <summary>
/// Detects the image pixel size. /// Reads the image base information.
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
/// <param name="config">the configuration.</param> /// <param name="config">the configuration.</param>
/// <returns> /// <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> /// </returns>
private static PixelTypeInfo InternalDetectPixelType(Stream stream, Configuration config) private static IImage InternalIdentity(Stream stream, Configuration config)
{ {
IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat _); var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector;
return decoder?.DetectPixelType(config, stream); return detector?.Identify(config, stream);
} }
} }
} }

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

@ -37,22 +37,22 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <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> /// </summary>
/// <param name="stream">The image stream to read the header from.</param> /// <param name="stream">The image stream to read the header from.</param>
/// <exception cref="NotSupportedException"> /// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable. /// Thrown if the stream is not readable nor seekable.
/// </exception> /// </exception>
/// <returns> /// <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> /// </returns>
public static PixelTypeInfo DetectPixelType(Stream stream) public static IImage Identify(Stream stream)
{ {
return DetectPixelType(null, stream); return Identify(null, stream);
} }
/// <summary> /// <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> /// </summary>
/// <param name="config">The configuration.</param> /// <param name="config">The configuration.</param>
/// <param name="stream">The image stream to read the header from.</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. /// Thrown if the stream is not readable nor seekable.
/// </exception> /// </exception>
/// <returns> /// <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> /// </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> /// <summary>

4
src/ImageSharp/Image/ImageFrameCollection.cs

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

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

@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
TestFile testFile = TestFile.Create(imagePath); TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) 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); TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) 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); TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) 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); TestFile testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) 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>(); return this.testFormat.Sample<TPixel>();
} }
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
{
throw new NotImplementedException();
}
public bool IsSupportedFileFormat(Span<byte> header) => testFormat.IsSupportedFileFormat(header); 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs 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(); 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)) 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); return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap<TPixel>(sourceBitmap);
} }
@ -32,12 +30,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
sourceBitmap.Height, sourceBitmap.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb)) 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.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 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); 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)) 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); return new Image<TPixel>(42, 42);
} }
public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream)
{
throw new NotImplementedException();
}
// Couldn't make xUnit happy without this hackery: // Couldn't make xUnit happy without this hackery:
private static ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>(); private static ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>();
@ -150,11 +145,6 @@ namespace SixLabors.ImageSharp.Tests
return new Image<TPixel>(42, 42); 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 static ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>();
private string callerName = null; private string callerName = null;

Loading…
Cancel
Save