From a50c3eb985c3d573d7433dbc888c43364e910faa Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 5 Sep 2018 20:07:19 +0100 Subject: [PATCH] Use dictionaries to store format specific metadata --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 5 +- src/ImageSharp/Formats/Bmp/BmpInfo.cs | 25 ------ .../Formats/Gif/GifColorTableMode.cs | 2 +- src/ImageSharp/Formats/Gif/GifConstants.cs | 18 +++- src/ImageSharp/Formats/Gif/GifDecoder.cs | 1 + src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 89 +++++++++++++------ ...DisposalMethod.cs => GifDisposalMethod.cs} | 2 +- src/ImageSharp/Formats/Gif/GifEncoder.cs | 2 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 54 +++++------ .../Formats/Gif/GifFrameMetaData.cs | 33 +++++++ src/ImageSharp/Formats/Gif/GifInfo.cs | 33 ------- src/ImageSharp/Formats/Gif/GifMetaData.cs | 29 ++++++ .../Formats/Gif/GifMetaDataExtensions.cs | 49 ++++++++++ .../Formats/Gif/IGifDecoderOptions.cs | 1 + .../Formats/Gif/IGifEncoderOptions.cs | 2 +- .../Sections/GifGraphicControlExtension.cs | 4 +- .../GifNetscapeLoopingApplicationExtension.cs | 44 +++++++++ .../Formats/Jpeg/JpegDecoderCore.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegInfo.cs | 25 ------ src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 +- src/ImageSharp/Formats/Png/PngInfo.cs | 25 ------ src/ImageSharp/ImageInfo.cs | 12 +-- .../Gif => MetaData}/FrameDecodingMode.cs | 2 +- src/ImageSharp/MetaData/ImageFrameMetaData.cs | 67 +++++++++----- src/ImageSharp/MetaData/ImageMetaData.cs | 69 ++++++++++---- .../Formats/Gif/GifEncoderTests.cs | 21 +++-- .../GifGraphicControlExtensionTests.cs | 8 +- tests/ImageSharp.Tests/ImageInfoTests.cs | 70 +-------------- .../MetaData/ImageFrameMetaDataTests.cs | 14 +-- .../MetaData/ImageMetaDataTests.cs | 2 - .../SystemDrawingReferenceDecoder.cs | 11 +-- 31 files changed, 399 insertions(+), 325 deletions(-) delete mode 100644 src/ImageSharp/Formats/Bmp/BmpInfo.cs rename src/ImageSharp/Formats/Gif/{DisposalMethod.cs => GifDisposalMethod.cs} (97%) create mode 100644 src/ImageSharp/Formats/Gif/GifFrameMetaData.cs delete mode 100644 src/ImageSharp/Formats/Gif/GifInfo.cs create mode 100644 src/ImageSharp/Formats/Gif/GifMetaData.cs create mode 100644 src/ImageSharp/Formats/Gif/GifMetaDataExtensions.cs create mode 100644 src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/JpegInfo.cs delete mode 100644 src/ImageSharp/Formats/Png/PngInfo.cs rename src/ImageSharp/{Formats/Gif => MetaData}/FrameDecodingMode.cs (91%) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 4cb524ecb..385c79896 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/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); } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpInfo.cs b/src/ImageSharp/Formats/Bmp/BmpInfo.cs deleted file mode 100644 index dfffe94db..000000000 --- a/src/ImageSharp/Formats/Bmp/BmpInfo.cs +++ /dev/null @@ -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 -{ - /// - /// Contains information about the bmp including dimensions, pixel type information and additional metadata. - /// - public class BmpInfo : ImageInfo - { - /// - /// Initializes a new instance of the class. - /// - /// The image pixel type information. - /// The size of the image in pixels. - /// The images metadata. - internal BmpInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData) - : base(pixelType, size, metaData) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifColorTableMode.cs b/src/ImageSharp/Formats/Gif/GifColorTableMode.cs index aa4192863..95b333562 100644 --- a/src/ImageSharp/Formats/Gif/GifColorTableMode.cs +++ b/src/ImageSharp/Formats/Gif/GifColorTableMode.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { /// - /// Provides enumeration for the available Gif color table modes. + /// Provides enumeration for the available color table modes. /// public enum GifColorTableMode { diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs index 0dbd39b99..cc80b0cce 100644 --- a/src/ImageSharp/Formats/Gif/GifConstants.cs +++ b/src/ImageSharp/Formats/Gif/GifConstants.cs @@ -26,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// internal static readonly byte[] MagicNumber = Encoding.UTF8.GetBytes(FileType + FileVersion); + /// + /// Gets the key used for storing and retriving metadata. + /// + public const string MetaDataKey = FileType; + /// /// The extension block introducer !. /// @@ -41,20 +46,25 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public const byte ApplicationExtensionLabel = 0xFF; + /// + /// The application block size. + /// + public const byte ApplicationBlockSize = 11; + /// /// The application identification. /// - public const string ApplicationIdentification = "NETSCAPE2.0"; + public const string NetscapeApplicationIdentification = "NETSCAPE2.0"; /// /// The ASCII encoded application identification bytes. /// - internal static readonly byte[] ApplicationIdentificationBytes = Encoding.UTF8.GetBytes(ApplicationIdentification); + internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.UTF8.GetBytes(NetscapeApplicationIdentification); /// - /// The application block size. + /// The Netscape looping application sub block size. /// - public const byte ApplicationBlockSize = 11; + public const byte NetscapeLoopingSubBlockSize = 3; /// /// The comment label. diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index ac451a355..42c76d640 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/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 diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index e79151752..092ac859d 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -61,10 +61,15 @@ namespace SixLabors.ImageSharp.Formats.Gif private GifImageDescriptor imageDescriptor; /// - /// The metadata + /// The abstract metadata. /// private ImageMetaData metaData; + /// + /// The gif specific metadata. + /// + private GifMetaData gifMetaData; + /// /// Initializes a new instance of the class. /// @@ -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); } + /// + /// Reads the application extension block parsing any animation information + /// if present. + /// + 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. + } + /// /// Skips the designated number of bytes in the stream. /// @@ -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); } /// @@ -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); diff --git a/src/ImageSharp/Formats/Gif/DisposalMethod.cs b/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs similarity index 97% rename from src/ImageSharp/Formats/Gif/DisposalMethod.cs rename to src/ImageSharp/Formats/Gif/GifDisposalMethod.cs index 5d3e1b4d8..982340db6 100644 --- a/src/ImageSharp/Formats/Gif/DisposalMethod.cs +++ b/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// in an animation sequence. /// section 23 /// - public enum DisposalMethod + public enum GifDisposalMethod { /// /// No disposal specified. diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index e8e28ccdd..9f376044d 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Gets or sets the color table mode: Global or local. /// - public GifColorTableMode ColorTableMode { get; set; } + public GifColorTableMode? ColorTableMode { get; set; } /// public void Encode(Image image, Stream stream) diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index ab0ee1fb6..6f0068f35 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/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 /// /// The color table mode: Global or local. /// - private readonly GifColorTableMode colorTableMode; + private GifColorTableMode? colorTableMode; /// /// A flag indicating whether to ingore the metadata when writing the image. @@ -56,6 +55,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// private int bitDepth; + /// + /// Gif specific meta data. + /// + private GifMetaData gifMetaData; + /// /// Initializes a new instance of the class. /// @@ -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 quantized = this.quantizer.CreateFrameQuantizer().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 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 { ImageFrame previousFrame = null; + GifFrameMetaData previousMeta = null; foreach (ImageFrame 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(frame.MetaData.ColorTableLength).QuantizeFrame(frame); + quantized = this.quantizer.CreateFrameQuantizer(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 /// The metadata of the image or frame. /// The index of the color in the color palette to make transparent. /// The stream to write to. - 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, diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs b/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs new file mode 100644 index 000000000..cc04d4831 --- /dev/null +++ b/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 +{ + /// + /// Provides Gif specific metadata information for the image frame. + /// + public class GifFrameMetaData + { + /// + /// 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. + /// + public int ColorTableLength { get; set; } + + /// + /// 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. + /// + public int FrameDelay { get; set; } + + /// + /// 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. + /// + public GifDisposalMethod DisposalMethod { get; set; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifInfo.cs b/src/ImageSharp/Formats/Gif/GifInfo.cs deleted file mode 100644 index 1c345a576..000000000 --- a/src/ImageSharp/Formats/Gif/GifInfo.cs +++ /dev/null @@ -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 -{ - /// - /// Contains information about the bmp including dimensions, pixel type information and additional metadata. - /// - public class GifInfo : ImageInfo - { - /// - /// Initializes a new instance of the class. - /// - /// The color table mode. - /// The image pixel type information. - /// The size of the image in pixels. - /// The images metadata. - internal GifInfo( - GifColorTableMode colorTableMode, - PixelTypeInfo pixelType, - Size size, - ImageMetaData metaData) - : base(pixelType, size, metaData) => this.ColorTableMode = colorTableMode; - - /// - /// Gets the color table mode. - /// - public GifColorTableMode ColorTableMode { get; } - } -} diff --git a/src/ImageSharp/Formats/Gif/GifMetaData.cs b/src/ImageSharp/Formats/Gif/GifMetaData.cs new file mode 100644 index 000000000..f58f5dff3 --- /dev/null +++ b/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 +{ + /// + /// Provides Gif specific metadata information for the image. + /// + public class GifMetaData + { + /// + /// Gets or sets the number of times any animation is repeated. + /// + /// 0 means to repeat indefinitely, count is set as play n + 1 times + /// + /// + public ushort RepeatCount { get; set; } + + /// + /// Gets or sets the color table mode. + /// + public GifColorTableMode ColorTableMode { get; set; } + + /// + /// Gets or sets the length of the global color table if present. + /// + public int GlobalColorTableLength { get; set; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifMetaDataExtensions.cs b/src/ImageSharp/Formats/Gif/GifMetaDataExtensions.cs new file mode 100644 index 000000000..04b542d2d --- /dev/null +++ b/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 +{ + /// + /// Extension methods for storing meta data specific to Gif images. + /// + public static class GifMetaDataExtensions + { + /// + /// Adds or updates the Gif specific meta data to the image. + /// + /// The image meta data. + /// The gif meta data. + public static void AddOrUpdateGifMetaData(this ImageMetaData meta, GifMetaData value) => meta.AddOrUpdateMetaData(GifConstants.MetaDataKey, value); + + /// + /// Gets the Gif format specific meta data from the image. + /// + /// The image meta data. + /// The or null. + public static GifMetaData GetGifMetaData(this ImageMetaData meta) + { + meta.TryGetMetaData(GifConstants.MetaDataKey, out GifMetaData value); + return value; + } + + /// + /// Adds or updates the Gif specific meta data to the image frame. + /// + /// The image meta data. + /// The gif meta data. + public static void AddOrUpdateGifFrameMetaData(this ImageFrameMetaData meta, GifFrameMetaData value) => meta.AddOrUpdateMetaData(GifConstants.MetaDataKey, value); + + /// + /// Gets the Gif format specific meta data from the image frame. + /// + /// The image meta data. + /// The or null. + public static GifFrameMetaData GetGifFrameMetaData(this ImageFrameMetaData meta) + { + meta.TryGetMetaData(GifConstants.MetaDataKey, out GifFrameMetaData value); + return value; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs index e99f09add..42c202a3d 100644 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ b/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 { diff --git a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs index bad6e0031..7dd558c80 100644 --- a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs @@ -29,6 +29,6 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Gets the color table mode: Global or local. /// - GifColorTableMode ColorTableMode { get; } + GifColorTableMode? ColorTableMode { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs index 7ec5f2030..cb548d687 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs +++ b/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. /// - public DisposalMethod DisposalMethod => (DisposalMethod)((this.Packed & 0x1C) >> 2); + public GifDisposalMethod DisposalMethod => (GifDisposalMethod)((this.Packed & 0x1C) >> 2); /// /// Gets a value indicating whether transparency flag is to be set. @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Gif return MemoryMarshal.Cast(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 diff --git a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs new file mode 100644 index 000000000..49a52edf6 --- /dev/null +++ b/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; + + /// + /// Gets the repeat count. + /// 0 means loop indefinitely. Count is set as play n + 1 times. + /// + public ushort RepeatCount { get; } + + public static GifNetscapeLoopingApplicationExtension Parse(ReadOnlySpan buffer) + { + ushort repeatCount = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2)); + return new GifNetscapeLoopingApplicationExtension(repeatCount); + } + + public int WriteTo(Span 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. + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index fb717d3fb..8c5230a1c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/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); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegInfo.cs b/src/ImageSharp/Formats/Jpeg/JpegInfo.cs deleted file mode 100644 index 8e9b6257c..000000000 --- a/src/ImageSharp/Formats/Jpeg/JpegInfo.cs +++ /dev/null @@ -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 -{ - /// - /// Contains information about the bmp including dimensions, pixel type information and additional metadata. - /// - public class JpegInfo : ImageInfo - { - /// - /// Initializes a new instance of the class. - /// - /// The image pixel type information. - /// The size of the image in pixels. - /// The images metadata. - internal JpegInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData) - : base(pixelType, size, metaData) - { - } - } -} diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 903256016..be1914174 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/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); } /// diff --git a/src/ImageSharp/Formats/Png/PngInfo.cs b/src/ImageSharp/Formats/Png/PngInfo.cs deleted file mode 100644 index 2bb0b16b7..000000000 --- a/src/ImageSharp/Formats/Png/PngInfo.cs +++ /dev/null @@ -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 -{ - /// - /// Contains information about the bmp including dimensions, pixel type information and additional metadata. - /// - public class PngInfo : ImageInfo - { - /// - /// Initializes a new instance of the class. - /// - /// The image pixel type information. - /// The size of the image in pixels. - /// The images metadata. - internal PngInfo(PixelTypeInfo pixelType, Size size, ImageMetaData metaData) - : base(pixelType, size, metaData) - { - } - } -} diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index eed1c6691..6f894cb59 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -3,26 +3,26 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.MetaData; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { /// /// Contains information about the image including dimensions, pixel type information and additional metadata /// - public abstract class ImageInfo : IImageInfo + internal sealed class ImageInfo : IImageInfo { /// /// Initializes a new instance of the class. /// /// The image pixel type information. - /// The size of the image in pixels. + /// The width of the image in pixels. + /// The height of the image in pixels. /// The images metadata. - 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; } diff --git a/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs b/src/ImageSharp/MetaData/FrameDecodingMode.cs similarity index 91% rename from src/ImageSharp/Formats/Gif/FrameDecodingMode.cs rename to src/ImageSharp/MetaData/FrameDecodingMode.cs index 05791c92e..2863fbf8f 100644 --- a/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs +++ b/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 { /// /// Enumerated frame process modes to apply to multi-frame images. diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs index f83e092c9..55678789e 100644 --- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs +++ b/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 /// public sealed class ImageFrameMetaData { + private readonly Dictionary metaData = new Dictionary(StringComparer.OrdinalIgnoreCase); + /// /// Initializes a new instance of the class. /// @@ -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 meta in other.metaData) + { + this.metaData.Add(meta.Key, meta.Value); + } } /// - /// 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. /// - public int ColorTableLength { get; set; } + /// The cloned instance. + public ImageFrameMetaData Clone() => new ImageFrameMetaData(this); /// - /// 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 . /// - public int FrameDelay { get; set; } + /// The key of the metadata to add. + /// The value of the element to add. + /// key is null. + /// value is null. + /// An element with the same key already exists in the . + public void AddOrUpdateMetaData(string key, object value) + { + // Don't think this needs to be threadsafe. + Guard.NotNull(value, nameof(value)); + this.metaData[key] = value; + } /// - /// 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. /// - public DisposalMethod DisposalMethod { get; set; } + /// The type of value. + /// The key of the value to get. + /// + /// 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. + /// + /// + /// true if the contains an element with + /// the specified key; otherwise, false. + /// + public bool TryGetMetaData(string key, out T value) + { + if (this.metaData.TryGetValue(key, out object meta)) + { + value = (T)meta; + return true; + } - /// - /// Clones this ImageFrameMetaData. - /// - /// The cloned instance. - public ImageFrameMetaData Clone() => new ImageFrameMetaData(this); + value = default; + return false; + } } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index 40880bd08..8233798c2 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/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 /// public const double DefaultVerticalResolution = 96; + private readonly Dictionary metaData = new Dictionary(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 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 Properties { get; } = new List(); /// - /// Gets or sets the number of times any animation is repeated. - /// 0 means to repeat indefinitely. + /// Adds or updates the specified key and value to the . + /// + /// The key of the metadata to add. + /// The value of the element to add. + /// key is null. + /// value is null. + /// An element with the same key already exists in the . + public void AddOrUpdateMetaData(string key, object value) + { + // Don't think this needs to be threadsafe. + Guard.NotNull(value, nameof(value)); + this.metaData[key] = value; + } + + /// + /// Gets the metadata value associated with the specified key. /// - public ushort RepeatCount { get; set; } + /// The type of value. + /// The key of the value to get. + /// + /// 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. + /// + /// + /// true if the contains an element with + /// the specified key; otherwise, false. + /// + public bool TryGetMetaData(string key, out T value) + { + if (this.metaData.TryGetValue(key, out object meta)) + { + value = (T)meta; + return true; + } + + value = default; + return false; + } + + /// + /// Clones this into a new instance + /// + /// The cloned metadata instance + public ImageMetaData Clone() => new ImageMetaData(this); /// /// Looks up a property with the provided name. @@ -153,21 +200,9 @@ namespace SixLabors.ImageSharp.MetaData return false; } - /// - /// Clones this into a new instance - /// - /// The cloned metadata instance - public ImageMetaData Clone() - { - return new ImageMetaData(this); - } - /// /// Synchronizes the profiles with the current meta data. /// - internal void SyncProfiles() - { - this.ExifProfile?.Sync(this); - } + internal void SyncProfiles() => this.ExifProfile?.Sync(this); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 11fa4d5d5..2b08bf20d 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/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(); diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs index 2790b1a57..dc0da5e2d 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs +++ b/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)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index d46e34052..91f6804c0 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/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); - } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs index 6e18fe253..c250d973b 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs +++ b/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); } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index 8934ebc36..d681c90ba 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/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] diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 3696accdd..427a56542 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/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()); } } }