Browse Source

Use dictionaries to store format specific metadata

pull/693/head
James Jackson-South 8 years ago
parent
commit
a50c3eb985
  1. 5
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 25
      src/ImageSharp/Formats/Bmp/BmpInfo.cs
  3. 2
      src/ImageSharp/Formats/Gif/GifColorTableMode.cs
  4. 18
      src/ImageSharp/Formats/Gif/GifConstants.cs
  5. 1
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  6. 89
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  7. 2
      src/ImageSharp/Formats/Gif/GifDisposalMethod.cs
  8. 2
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  9. 54
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  10. 33
      src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
  11. 33
      src/ImageSharp/Formats/Gif/GifInfo.cs
  12. 29
      src/ImageSharp/Formats/Gif/GifMetaData.cs
  13. 49
      src/ImageSharp/Formats/Gif/GifMetaDataExtensions.cs
  14. 1
      src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
  15. 2
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  16. 4
      src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs
  17. 44
      src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
  18. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  19. 25
      src/ImageSharp/Formats/Jpeg/JpegInfo.cs
  20. 3
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  21. 25
      src/ImageSharp/Formats/Png/PngInfo.cs
  22. 12
      src/ImageSharp/ImageInfo.cs
  23. 2
      src/ImageSharp/MetaData/FrameDecodingMode.cs
  24. 67
      src/ImageSharp/MetaData/ImageFrameMetaData.cs
  25. 69
      src/ImageSharp/MetaData/ImageMetaData.cs
  26. 21
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  27. 8
      tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs
  28. 70
      tests/ImageSharp.Tests/ImageInfoTests.cs
  29. 14
      tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs
  30. 2
      tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs
  31. 11
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs

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

@ -10,7 +10,6 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Bmp
{
@ -165,9 +164,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public IImageInfo Identify(Stream stream)
{
this.ReadImageHeaders(stream, out _, out _);
var size = new Size(this.infoHeader.Width, this.infoHeader.Height);
return new BmpInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), size, this.metaData);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metaData);
}
/// <summary>

25
src/ImageSharp/Formats/Bmp/BmpInfo.cs

@ -1,25 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Contains information about the bmp including dimensions, pixel type information and additional metadata.
/// </summary>
public class BmpInfo : ImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="BmpInfo" /> class.
/// </summary>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="size">The size of the image in pixels.</param>
/// <param name="metaData">The images metadata.</param>
internal BmpInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData)
: base(pixelType, size, metaData)
{
}
}
}

2
src/ImageSharp/Formats/Gif/GifColorTableMode.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Provides enumeration for the available Gif color table modes.
/// Provides enumeration for the available color table modes.
/// </summary>
public enum GifColorTableMode
{

18
src/ImageSharp/Formats/Gif/GifConstants.cs

@ -26,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
internal static readonly byte[] MagicNumber = Encoding.UTF8.GetBytes(FileType + FileVersion);
/// <summary>
/// Gets the key used for storing and retriving metadata.
/// </summary>
public const string MetaDataKey = FileType;
/// <summary>
/// The extension block introducer <value>!</value>.
/// </summary>
@ -41,20 +46,25 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public const byte ApplicationExtensionLabel = 0xFF;
/// <summary>
/// The application block size.
/// </summary>
public const byte ApplicationBlockSize = 11;
/// <summary>
/// The application identification.
/// </summary>
public const string ApplicationIdentification = "NETSCAPE2.0";
public const string NetscapeApplicationIdentification = "NETSCAPE2.0";
/// <summary>
/// The ASCII encoded application identification bytes.
/// </summary>
internal static readonly byte[] ApplicationIdentificationBytes = Encoding.UTF8.GetBytes(ApplicationIdentification);
internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.UTF8.GetBytes(NetscapeApplicationIdentification);
/// <summary>
/// The application block size.
/// The Netscape looping application sub block size.
/// </summary>
public const byte ApplicationBlockSize = 11;
public const byte NetscapeLoopingSubBlockSize = 3;
/// <summary>
/// The comment label.

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

@ -3,6 +3,7 @@
using System.IO;
using System.Text;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Gif

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

@ -61,10 +61,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
private GifImageDescriptor imageDescriptor;
/// <summary>
/// The metadata
/// The abstract metadata.
/// </summary>
private ImageMetaData metaData;
/// <summary>
/// The gif specific metadata.
/// </summary>
private GifMetaData gifMetaData;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary>
@ -134,11 +139,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadComments();
break;
case GifConstants.ApplicationExtensionLabel:
// 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.
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
@ -163,6 +164,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.globalColorTable?.Dispose();
}
image?.MetaData.AddOrUpdateGifMetaData(this.gifMetaData);
return image;
}
@ -197,11 +199,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadComments();
break;
case GifConstants.ApplicationExtensionLabel:
// 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.
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
@ -226,16 +224,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.globalColorTable?.Dispose();
}
GifColorTableMode colorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag
? GifColorTableMode.Global
: GifColorTableMode.Local;
var size = new Size(this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height);
return new GifInfo(
colorTableMode,
this.metaData.AddOrUpdateGifMetaData(this.gifMetaData);
return new ImageInfo(
new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel),
size,
this.logicalScreenDescriptor.Width,
this.logicalScreenDescriptor.Height,
this.metaData);
}
@ -269,6 +262,41 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
}
/// <summary>
/// Reads the application extension block parsing any animation information
/// if present.
/// </summary>
private void ReadApplicationExtension()
{
int appLength = this.stream.ReadByte();
// If the length is 11 then it's a valid extension and most likely
// a NETSCAPE or ANIMEXTS extension. We want the loop count from this.
if (appLength == GifConstants.ApplicationBlockSize)
{
this.stream.Skip(appLength);
int subBlockSize = this.stream.ReadByte();
// TODO: There's also a NETSCAPE buffer extension.
// http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension
if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize)
{
this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
this.gifMetaData.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
this.stream.Skip(1); // Skip the terminator.
return;
}
// Could be XMP or something else not supported yet.
// Back up and skip.
this.stream.Position -= appLength + 1;
this.Skip(appLength);
return;
}
this.Skip(appLength); // Not supported by any known decoder.
}
/// <summary>
/// Skips the designated number of bytes in the stream.
/// </summary>
@ -399,7 +427,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
if (this.graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToPrevious)
{
prevFrame = previousFrame;
}
@ -482,7 +510,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
previousFrame = currentFrame ?? image.Frames.RootFrame;
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
if (this.graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToBackground)
{
this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height);
}
@ -514,24 +542,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetaData(ImageFrameMetaData meta)
{
var gifMeta = new GifFrameMetaData();
if (this.graphicsControlExtension.DelayTime > 0)
{
meta.FrameDelay = this.graphicsControlExtension.DelayTime;
gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime;
}
// Frames can either use the global table or their own local table.
if (this.logicalScreenDescriptor.GlobalColorTableFlag
&& this.logicalScreenDescriptor.GlobalColorTableSize > 0)
{
meta.ColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize;
gifMeta.ColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize;
}
else if (this.imageDescriptor.LocalColorTableFlag
&& this.imageDescriptor.LocalColorTableSize > 0)
{
meta.ColorTableLength = this.imageDescriptor.LocalColorTableSize;
gifMeta.ColorTableLength = this.imageDescriptor.LocalColorTableSize;
}
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
gifMeta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
meta.AddOrUpdateGifFrameMetaData(gifMeta);
}
/// <summary>
@ -575,10 +605,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
this.metaData = meta;
this.gifMetaData = new GifMetaData
{
ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag
? GifColorTableMode.Global
: GifColorTableMode.Local
};
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.gifMetaData.GlobalColorTableLength = globalColorTableLength;
this.globalColorTable = this.MemoryAllocator.AllocateManagedByteBuffer(globalColorTableLength, AllocationOptions.Clean);

2
src/ImageSharp/Formats/Gif/DisposalMethod.cs → src/ImageSharp/Formats/Gif/GifDisposalMethod.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// in an animation sequence.
/// <see href="http://www.w3.org/Graphics/GIF/spec-gif89a.txt"/> section 23
/// </summary>
public enum DisposalMethod
public enum GifDisposalMethod
{
/// <summary>
/// No disposal specified.

2
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Gets or sets the color table mode: Global or local.
/// </summary>
public GifColorTableMode ColorTableMode { get; set; }
public GifColorTableMode? ColorTableMode { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)

54
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -44,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The color table mode: Global or local.
/// </summary>
private readonly GifColorTableMode colorTableMode;
private GifColorTableMode? colorTableMode;
/// <summary>
/// A flag indicating whether to ingore the metadata when writing the image.
@ -56,6 +55,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private int bitDepth;
/// <summary>
/// Gif specific meta data.
/// </summary>
private GifMetaData gifMetaData;
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary>
@ -66,7 +70,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.memoryAllocator = memoryAllocator;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
this.colorTableMode = options.ColorTableMode;
this.ignoreMetadata = options.IgnoreMetadata;
}
@ -82,6 +85,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.gifMetaData = image.MetaData.GetGifMetaData() ?? new GifMetaData();
this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode;
bool useGlobalTable = this.colorTableMode.Equals(GifColorTableMode.Global);
// Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized =
this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
@ -94,7 +101,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Write the LSD.
int index = this.GetTransparentIndex(quantized);
bool useGlobalTable = this.colorTableMode.Equals(GifColorTableMode.Global);
this.WriteLogicalScreenDescriptor(image, index, useGlobalTable, stream);
if (useGlobalTable)
@ -108,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Write application extension to allow additional frames.
if (image.Frames.Count > 1)
{
this.WriteApplicationExtension(stream, image.MetaData.RepeatCount);
this.WriteApplicationExtension(stream, this.gifMetaData.RepeatCount);
}
if (useGlobalTable)
@ -136,8 +142,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
this.WriteGraphicalControlExtension(frame.MetaData, transparencyIndex, stream);
GifFrameMetaData frameMetaData = frame.MetaData.GetGifFrameMetaData() ?? new GifFrameMetaData();
this.WriteGraphicalControlExtension(frameMetaData, transparencyIndex, stream);
this.WriteImageDescriptor(frame, false, stream);
if (i == 0)
@ -159,16 +165,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
where TPixel : struct, IPixel<TPixel>
{
ImageFrame<TPixel> previousFrame = null;
GifFrameMetaData previousMeta = null;
foreach (ImageFrame<TPixel> frame in image.Frames)
{
GifFrameMetaData meta = frame.MetaData.GetGifFrameMetaData() ?? new GifFrameMetaData();
if (quantized is null)
{
// Allow each frame to be encoded at whatever color depth the frame designates if set.
if (previousFrame != null
&& previousFrame.MetaData.ColorTableLength != frame.MetaData.ColorTableLength
&& frame.MetaData.ColorTableLength > 0)
&& previousMeta.ColorTableLength != meta.ColorTableLength
&& meta.ColorTableLength > 0)
{
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(frame.MetaData.ColorTableLength).QuantizeFrame(frame);
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(meta.ColorTableLength).QuantizeFrame(frame);
}
else
{
@ -177,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
this.WriteGraphicalControlExtension(frame.MetaData, this.GetTransparentIndex(quantized), stream);
this.WriteGraphicalControlExtension(meta, this.GetTransparentIndex(quantized), stream);
this.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
@ -185,6 +193,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
quantized?.Dispose();
quantized = null; // So next frame can regenerate it
previousFrame = frame;
previousMeta = meta;
}
}
@ -290,25 +299,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Application Extension Header
if (repeatCount != 1)
{
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.ApplicationExtensionLabel;
this.buffer[2] = GifConstants.ApplicationBlockSize;
// Write NETSCAPE2.0
GifConstants.ApplicationIdentificationBytes.AsSpan().CopyTo(this.buffer.AsSpan(3, 11));
// Application Data ----
this.buffer[14] = 3; // Application block length
this.buffer[15] = 1; // Data sub-block index (always 1)
// 0 means loop indefinitely. Count is set as play n + 1 times.
repeatCount = (ushort)Math.Max(0, repeatCount - 1);
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer.AsSpan(16, 2), repeatCount); // Repeat count for images.
this.buffer[18] = GifConstants.Terminator; // Terminator
stream.Write(this.buffer, 0, 19);
var loopingExtension = new GifNetscapeLoopingApplicationExtension(repeatCount);
this.WriteExtension(loopingExtension, stream);
}
}
@ -348,7 +340,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="metaData">The metadata of the image or frame.</param>
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, int transparencyIndex, Stream stream)
private void WriteGraphicalControlExtension(GifFrameMetaData metaData, int transparencyIndex, Stream stream)
{
byte packedValue = GifGraphicControlExtension.GetPackedValue(
disposalMethod: metaData.DisposalMethod,

33
src/ImageSharp/Formats/Gif/GifFrameMetaData.cs

@ -0,0 +1,33 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Provides Gif specific metadata information for the image frame.
/// </summary>
public class GifFrameMetaData
{
/// <summary>
/// Gets or sets the length of the color table for paletted images.
/// If not 0, then this field indicates the maximum number of colors to use when quantizing the
/// image frame.
/// </summary>
public int ColorTableLength { get; set; }
/// <summary>
/// Gets or sets the frame delay for animated images.
/// If not 0, when utilized in Gif animation, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
public int FrameDelay { get; set; }
/// <summary>
/// Gets or sets the disposal method for animated images.
/// Primarily used in Gif animation, this field indicates the way in which the graphic is to
/// be treated after being displayed.
/// </summary>
public GifDisposalMethod DisposalMethod { get; set; }
}
}

33
src/ImageSharp/Formats/Gif/GifInfo.cs

@ -1,33 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Contains information about the bmp including dimensions, pixel type information and additional metadata.
/// </summary>
public class GifInfo : ImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="GifInfo" /> class.
/// </summary>
/// <param name="colorTableMode">The color table mode.</param>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="size">The size of the image in pixels.</param>
/// <param name="metaData">The images metadata.</param>
internal GifInfo(
GifColorTableMode colorTableMode,
PixelTypeInfo pixelType,
Size size,
ImageMetaData metaData)
: base(pixelType, size, metaData) => this.ColorTableMode = colorTableMode;
/// <summary>
/// Gets the color table mode.
/// </summary>
public GifColorTableMode ColorTableMode { get; }
}
}

29
src/ImageSharp/Formats/Gif/GifMetaData.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Provides Gif specific metadata information for the image.
/// </summary>
public class GifMetaData
{
/// <summary>
/// Gets or sets the number of times any animation is repeated.
/// <remarks>
/// 0 means to repeat indefinitely, count is set as play n + 1 times
/// </remarks>
/// </summary>
public ushort RepeatCount { get; set; }
/// <summary>
/// Gets or sets the color table mode.
/// </summary>
public GifColorTableMode ColorTableMode { get; set; }
/// <summary>
/// Gets or sets the length of the global color table if present.
/// </summary>
public int GlobalColorTableLength { get; set; }
}
}

49
src/ImageSharp/Formats/Gif/GifMetaDataExtensions.cs

@ -0,0 +1,49 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Extension methods for storing meta data specific to Gif images.
/// </summary>
public static class GifMetaDataExtensions
{
/// <summary>
/// Adds or updates the Gif specific meta data to the image.
/// </summary>
/// <param name="meta">The image meta data.</param>
/// <param name="value">The gif meta data.</param>
public static void AddOrUpdateGifMetaData(this ImageMetaData meta, GifMetaData value) => meta.AddOrUpdateMetaData(GifConstants.MetaDataKey, value);
/// <summary>
/// Gets the Gif format specific meta data from the image.
/// </summary>
/// <param name="meta">The image meta data.</param>
/// <returns>The <see cref="GifMetaData"/> or null.</returns>
public static GifMetaData GetGifMetaData(this ImageMetaData meta)
{
meta.TryGetMetaData(GifConstants.MetaDataKey, out GifMetaData value);
return value;
}
/// <summary>
/// Adds or updates the Gif specific meta data to the image frame.
/// </summary>
/// <param name="meta">The image meta data.</param>
/// <param name="value">The gif meta data.</param>
public static void AddOrUpdateGifFrameMetaData(this ImageFrameMetaData meta, GifFrameMetaData value) => meta.AddOrUpdateMetaData(GifConstants.MetaDataKey, value);
/// <summary>
/// Gets the Gif format specific meta data from the image frame.
/// </summary>
/// <param name="meta">The image meta data.</param>
/// <returns>The <see cref="GifMetaData"/> or null.</returns>
public static GifFrameMetaData GetGifFrameMetaData(this ImageFrameMetaData meta)
{
meta.TryGetMetaData(GifConstants.MetaDataKey, out GifFrameMetaData value);
return value;
}
}
}

1
src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Text;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Gif
{

2
src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs

@ -29,6 +29,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Gets the color table mode: Global or local.
/// </summary>
GifColorTableMode ColorTableMode { get; }
GifColorTableMode? ColorTableMode { get; }
}
}

4
src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Gets the disposal method which indicates the way in which the
/// graphic is to be treated after being displayed.
/// </summary>
public DisposalMethod DisposalMethod => (DisposalMethod)((this.Packed & 0x1C) >> 2);
public GifDisposalMethod DisposalMethod => (GifDisposalMethod)((this.Packed & 0x1C) >> 2);
/// <summary>
/// Gets a value indicating whether transparency flag is to be set.
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
return MemoryMarshal.Cast<byte, GifGraphicControlExtension>(buffer)[0];
}
public static byte GetPackedValue(DisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false)
public static byte GetPackedValue(GifDisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false)
{
/*
Reserved | 3 Bits

44
src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
namespace SixLabors.ImageSharp.Formats.Gif
{
internal readonly struct GifNetscapeLoopingApplicationExtension : IGifExtension
{
public GifNetscapeLoopingApplicationExtension(ushort repeatCount) => this.RepeatCount = repeatCount;
public byte Label => GifConstants.ApplicationExtensionLabel;
/// <summary>
/// Gets the repeat count.
/// 0 means loop indefinitely. Count is set as play n + 1 times.
/// </summary>
public ushort RepeatCount { get; }
public static GifNetscapeLoopingApplicationExtension Parse(ReadOnlySpan<byte> buffer)
{
ushort repeatCount = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2));
return new GifNetscapeLoopingApplicationExtension(repeatCount);
}
public int WriteTo(Span<byte> buffer)
{
buffer[0] = GifConstants.ApplicationBlockSize;
// Write NETSCAPE2.0
GifConstants.NetscapeApplicationIdentificationBytes.AsSpan().CopyTo(buffer.Slice(1, 11));
// Application Data ----
buffer[12] = 3; // Application block length (always 3)
buffer[13] = 1; // Data sub-block indentity (always 1)
// 0 means loop indefinitely. Count is set as play n + 1 times.
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14, 2), this.RepeatCount);
return 16; // Length - Introducer + Label + Terminator.
}
}
}

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

@ -235,7 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InitIccProfile();
this.InitDerivedMetaDataProperties();
return new JpegInfo(new PixelTypeInfo(this.BitsPerPixel), new Size(this.ImageWidth, this.ImageHeight), this.MetaData);
return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
}
/// <summary>

25
src/ImageSharp/Formats/Jpeg/JpegInfo.cs

@ -1,25 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
/// Contains information about the bmp including dimensions, pixel type information and additional metadata.
/// </summary>
public class JpegInfo : ImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="JpegInfo" /> class.
/// </summary>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="size">The size of the image in pixels.</param>
/// <param name="metaData">The images metadata.</param>
internal JpegInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData)
: base(pixelType, size, metaData)
{
}
}
}

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

@ -17,7 +17,6 @@ using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Png
{
@ -349,7 +348,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("PNG Image does not contain a header chunk");
}
return new PngInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new Size(this.header.Width, this.header.Height), metadata);
return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
}
/// <summary>

25
src/ImageSharp/Formats/Png/PngInfo.cs

@ -1,25 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Contains information about the bmp including dimensions, pixel type information and additional metadata.
/// </summary>
public class PngInfo : ImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="PngInfo" /> class.
/// </summary>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="size">The size of the image in pixels.</param>
/// <param name="metaData">The images metadata.</param>
internal PngInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData)
: base(pixelType, size, metaData)
{
}
}
}

12
src/ImageSharp/ImageInfo.cs

@ -3,26 +3,26 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Contains information about the image including dimensions, pixel type information and additional metadata
/// </summary>
public abstract class ImageInfo : IImageInfo
internal sealed class ImageInfo : IImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageInfo"/> class.
/// </summary>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="size">The size 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="metaData">The images metadata.</param>
protected ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData)
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData)
{
this.PixelType = pixelType;
this.Width = size.Width;
this.Height = size.Height;
this.Width = width;
this.Height = height;
this.MetaData = metaData;
}

2
src/ImageSharp/Formats/Gif/FrameDecodingMode.cs → src/ImageSharp/MetaData/FrameDecodingMode.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
namespace SixLabors.ImageSharp.MetaData
{
/// <summary>
/// Enumerated frame process modes to apply to multi-frame images.

67
src/ImageSharp/MetaData/ImageFrameMetaData.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Gif;
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.MetaData
{
@ -10,6 +11,8 @@ namespace SixLabors.ImageSharp.MetaData
/// </summary>
public sealed class ImageFrameMetaData
{
private readonly Dictionary<string, object> metaData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrameMetaData"/> class.
/// </summary>
@ -28,37 +31,57 @@ namespace SixLabors.ImageSharp.MetaData
{
DebugGuard.NotNull(other, nameof(other));
this.ColorTableLength = other.ColorTableLength;
this.FrameDelay = other.FrameDelay;
this.DisposalMethod = other.DisposalMethod;
foreach (KeyValuePair<string, object> meta in other.metaData)
{
this.metaData.Add(meta.Key, meta.Value);
}
}
/// <summary>
/// Gets or sets the length of the color table for paletted images.
/// If not 0, then this field indicates the maximum number of colors to use when quantizing the
/// image frame.
/// Clones this ImageFrameMetaData.
/// </summary>
public int ColorTableLength { get; set; }
/// <returns>The cloned instance.</returns>
public ImageFrameMetaData Clone() => new ImageFrameMetaData(this);
/// <summary>
/// Gets or sets the frame delay for animated images.
/// If not 0, when utilized in Gif animation, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// Adds or updates the specified key and value to the <see cref="ImageMetaData"/>.
/// </summary>
public int FrameDelay { get; set; }
/// <param name="key">The key of the metadata to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="ArgumentNullException">key is null.</exception>
/// <exception cref="ArgumentNullException">value is null.</exception>
/// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="ImageMetaData"/>.</exception>
public void AddOrUpdateMetaData(string key, object value)
{
// Don't think this needs to be threadsafe.
Guard.NotNull(value, nameof(value));
this.metaData[key] = value;
}
/// <summary>
/// Gets or sets the disposal method for animated images.
/// Primarily used in Gif animation, this field indicates the way in which the graphic is to
/// be treated after being displayed.
/// Gets the metadata value associated with the specified key.
/// </summary>
public DisposalMethod DisposalMethod { get; set; }
/// <typeparam name="T">The type of value.</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <param name="value">
/// When this method returns, contains the metadata value associated with the specified key,
/// if the key is found; otherwise, the default value for the type of the value parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns>
/// true if the <see cref="ImageMetaData"/> contains an element with
/// the specified key; otherwise, false.
/// </returns>
public bool TryGetMetaData<T>(string key, out T value)
{
if (this.metaData.TryGetValue(key, out object meta))
{
value = (T)meta;
return true;
}
/// <summary>
/// Clones this ImageFrameMetaData.
/// </summary>
/// <returns>The cloned instance.</returns>
public ImageFrameMetaData Clone() => new ImageFrameMetaData(this);
value = default;
return false;
}
}
}

69
src/ImageSharp/MetaData/ImageMetaData.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
@ -24,6 +25,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </summary>
public const double DefaultVerticalResolution = 96;
private readonly Dictionary<string, object> metaData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
private double horizontalResolution;
private double verticalResolution;
@ -48,7 +50,11 @@ namespace SixLabors.ImageSharp.MetaData
this.HorizontalResolution = other.HorizontalResolution;
this.VerticalResolution = other.VerticalResolution;
this.ResolutionUnits = other.ResolutionUnits;
this.RepeatCount = other.RepeatCount;
foreach (KeyValuePair<string, object> meta in other.metaData)
{
this.metaData.Add(meta.Key, meta.Value);
}
foreach (ImageProperty property in other.Properties)
{
@ -125,10 +131,51 @@ namespace SixLabors.ImageSharp.MetaData
public IList<ImageProperty> Properties { get; } = new List<ImageProperty>();
/// <summary>
/// Gets or sets the number of times any animation is repeated.
/// <remarks>0 means to repeat indefinitely.</remarks>
/// Adds or updates the specified key and value to the <see cref="ImageMetaData"/>.
/// </summary>
/// <param name="key">The key of the metadata to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="ArgumentNullException">key is null.</exception>
/// <exception cref="ArgumentNullException">value is null.</exception>
/// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="ImageMetaData"/>.</exception>
public void AddOrUpdateMetaData(string key, object value)
{
// Don't think this needs to be threadsafe.
Guard.NotNull(value, nameof(value));
this.metaData[key] = value;
}
/// <summary>
/// Gets the metadata value associated with the specified key.
/// </summary>
public ushort RepeatCount { get; set; }
/// <typeparam name="T">The type of value.</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <param name="value">
/// When this method returns, contains the metadata value associated with the specified key,
/// if the key is found; otherwise, the default value for the type of the value parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns>
/// true if the <see cref="ImageMetaData"/> contains an element with
/// the specified key; otherwise, false.
/// </returns>
public bool TryGetMetaData<T>(string key, out T value)
{
if (this.metaData.TryGetValue(key, out object meta))
{
value = (T)meta;
return true;
}
value = default;
return false;
}
/// <summary>
/// Clones this into a new instance
/// </summary>
/// <returns>The cloned metadata instance</returns>
public ImageMetaData Clone() => new ImageMetaData(this);
/// <summary>
/// Looks up a property with the provided name.
@ -153,21 +200,9 @@ namespace SixLabors.ImageSharp.MetaData
return false;
}
/// <summary>
/// Clones this into a new instance
/// </summary>
/// <returns>The cloned metadata instance</returns>
public ImageMetaData Clone()
{
return new ImageMetaData(this);
}
/// <summary>
/// Synchronizes the profiles with the current meta data.
/// </summary>
internal void SyncProfiles()
{
this.ExifProfile?.Sync(this);
}
internal void SyncProfiles() => this.ExifProfile?.Sync(this);
}
}

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

@ -186,32 +186,37 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
using (var inStream = new MemoryStream(TestFile.Create(TestImages.Gif.Leo).Bytes))
using (var outStream = new MemoryStream())
{
var info = (GifInfo)Image.Identify(inStream);
GifColorTableMode colorMode = info.ColorTableMode;
inStream.Position = 0;
var image = Image.Load(inStream);
GifMetaData metaData = image.MetaData.GetGifMetaData();
GifFrameMetaData frameMetaData = image.Frames.RootFrame.MetaData.GetGifFrameMetaData();
GifColorTableMode colorMode = metaData.ColorTableMode;
var encoder = new GifEncoder()
{
ColorTableMode = colorMode,
Quantizer = new OctreeQuantizer(image.Frames.RootFrame.MetaData.ColorTableLength)
Quantizer = new OctreeQuantizer(frameMetaData.ColorTableLength)
};
image.Save(outStream, encoder);
outStream.Position = 0;
var cloneInfo = (GifInfo)Image.Identify(outStream);
outStream.Position = 0;
var clone = Image.Load(outStream);
GifMetaData cloneMetaData = clone.MetaData.GetGifMetaData();
Assert.Equal(metaData.ColorTableMode, cloneMetaData.ColorTableMode);
// Gifiddle and Cyotek GifInfo say this image has 64 colors.
Assert.Equal(64, image.Frames.RootFrame.MetaData.ColorTableLength);
Assert.Equal(info.ColorTableMode, cloneInfo.ColorTableMode);
Assert.Equal(64, frameMetaData.ColorTableLength);
for (int i = 0; i < image.Frames.Count; i++)
{
Assert.Equal(image.Frames[i].MetaData.ColorTableLength, clone.Frames[i].MetaData.ColorTableLength);
Assert.Equal(image.Frames[i].MetaData.FrameDelay, clone.Frames[i].MetaData.FrameDelay);
GifFrameMetaData ifm = image.Frames[i].MetaData.GetGifFrameMetaData();
GifFrameMetaData cifm = clone.Frames[i].MetaData.GetGifFrameMetaData();
Assert.Equal(ifm.ColorTableLength, cifm.ColorTableLength);
Assert.Equal(ifm.FrameDelay, cifm.FrameDelay);
}
image.Dispose();

8
tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs

@ -12,10 +12,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
[Fact]
public void TestPackedValue()
{
Assert.Equal(0, GifGraphicControlExtension.GetPackedValue(DisposalMethod.Unspecified, false, false));
Assert.Equal(11, GifGraphicControlExtension.GetPackedValue(DisposalMethod.RestoreToBackground, true, true));
Assert.Equal(4, GifGraphicControlExtension.GetPackedValue(DisposalMethod.NotDispose, false, false));
Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(DisposalMethod.RestoreToPrevious, true, false));
Assert.Equal(0, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.Unspecified, false, false));
Assert.Equal(11, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.RestoreToBackground, true, true));
Assert.Equal(4, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.NotDispose, false, false));
Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.RestoreToPrevious, true, false));
}
}
}

70
tests/ImageSharp.Tests/ImageInfoTests.cs

@ -2,10 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.MetaData;
using SixLabors.Primitives;
@ -16,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests
public class ImageInfoTests
{
[Fact]
public void JpegInfoInitializesCorrectly()
public void ImageInfoInitializesCorrectly()
{
const int Width = 50;
const int Height = 60;
@ -25,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests
var pixelType = new PixelTypeInfo(8);
var meta = new ImageMetaData();
var info = new JpegInfo(pixelType, size, meta);
var info = new ImageInfo(pixelType, Width, Height, meta);
Assert.Equal(pixelType, info.PixelType);
Assert.Equal(Width, info.Width);
@ -34,67 +30,5 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(rectangle, info.Bounds());
Assert.Equal(meta, info.MetaData);
}
[Fact]
public void BmpInfoInitializesCorrectly()
{
const int Width = 50;
const int Height = 60;
var size = new Size(Width, Height);
var rectangle = new Rectangle(0, 0, Width, Height);
var pixelType = new PixelTypeInfo(8);
var meta = new ImageMetaData();
var info = new BmpInfo(pixelType, size, meta);
Assert.Equal(pixelType, info.PixelType);
Assert.Equal(Width, info.Width);
Assert.Equal(Height, info.Height);
Assert.Equal(size, info.Size());
Assert.Equal(rectangle, info.Bounds());
Assert.Equal(meta, info.MetaData);
}
[Fact]
public void PngInfoInitializesCorrectly()
{
const int Width = 50;
const int Height = 60;
var size = new Size(Width, Height);
var rectangle = new Rectangle(0, 0, Width, Height);
var pixelType = new PixelTypeInfo(8);
var meta = new ImageMetaData();
var info = new PngInfo(pixelType, size, meta);
Assert.Equal(pixelType, info.PixelType);
Assert.Equal(Width, info.Width);
Assert.Equal(Height, info.Height);
Assert.Equal(size, info.Size());
Assert.Equal(rectangle, info.Bounds());
Assert.Equal(meta, info.MetaData);
}
[Fact]
public void GifInfoInitializesCorrectly()
{
const GifColorTableMode mode = GifColorTableMode.Local;
const int Width = 50;
const int Height = 60;
var size = new Size(Width, Height);
var rectangle = new Rectangle(0, 0, Width, Height);
var pixelType = new PixelTypeInfo(8);
var meta = new ImageMetaData();
var info = new GifInfo(mode, pixelType, size, meta);
Assert.Equal(mode, info.ColorTableMode);
Assert.Equal(pixelType, info.PixelType);
Assert.Equal(Width, info.Width);
Assert.Equal(Height, info.Height);
Assert.Equal(size, info.Size());
Assert.Equal(rectangle, info.Bounds());
Assert.Equal(meta, info.MetaData);
}
}
}

14
tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs

@ -17,20 +17,24 @@ namespace SixLabors.ImageSharp.Tests
{
const int frameDelay = 42;
const int colorTableLength = 128;
const DisposalMethod disposalMethod = DisposalMethod.RestoreToBackground;
const GifDisposalMethod disposalMethod = GifDisposalMethod.RestoreToBackground;
var metaData = new ImageFrameMetaData
var gifFrameMetaData = new GifFrameMetaData
{
FrameDelay = frameDelay,
ColorTableLength = colorTableLength,
DisposalMethod = disposalMethod
};
var metaData = new ImageFrameMetaData();
metaData.AddOrUpdateGifFrameMetaData(gifFrameMetaData);
var clone = new ImageFrameMetaData(metaData);
GifFrameMetaData cloneGifFrameMetaData = clone.GetGifFrameMetaData();
Assert.Equal(frameDelay, clone.FrameDelay);
Assert.Equal(colorTableLength, clone.ColorTableLength);
Assert.Equal(disposalMethod, clone.DisposalMethod);
Assert.Equal(frameDelay, cloneGifFrameMetaData.FrameDelay);
Assert.Equal(colorTableLength, cloneGifFrameMetaData.ColorTableLength);
Assert.Equal(disposalMethod, cloneGifFrameMetaData.DisposalMethod);
}
}
}

2
tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs

@ -28,7 +28,6 @@ namespace SixLabors.ImageSharp.Tests
metaData.HorizontalResolution = 4;
metaData.VerticalResolution = 2;
metaData.Properties.Add(imageProperty);
metaData.RepeatCount = 1;
ImageMetaData clone = metaData.Clone();
@ -36,7 +35,6 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(4, clone.HorizontalResolution);
Assert.Equal(2, clone.VerticalResolution);
Assert.Equal(imageProperty, clone.Properties[0]);
Assert.Equal(1, clone.RepeatCount);
}
[Fact]

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

@ -48,16 +48,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
using (var sourceBitmap = new System.Drawing.Bitmap(stream))
{
var pixelType = new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat));
var size = new SixLabors.Primitives.Size(sourceBitmap.Width, sourceBitmap.Height);
return new SystemDrawingInfo(pixelType, size, new ImageMetaData());
}
}
private class SystemDrawingInfo : ImageInfo
{
public SystemDrawingInfo(PixelTypeInfo pixelType, SixLabors.Primitives.Size size, ImageMetaData metaData)
: base(pixelType, size, metaData)
{
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetaData());
}
}
}

Loading…
Cancel
Save