From bacac401151810ae2a79c8e7e04c364a465ed8f9 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 5 Feb 2017 12:18:03 +0100 Subject: [PATCH] Added metadata class for image frames. --- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 27 ++++++++---- src/ImageSharp.Formats.Gif/GifEncoderCore.cs | 31 ++++++++++++- src/ImageSharp/Image/IImageBase.cs | 8 ---- src/ImageSharp/Image/IImageFrame.cs | 18 ++++++++ src/ImageSharp/Image/ImageBase{TColor}.cs | 4 -- src/ImageSharp/Image/ImageFrame{TColor}.cs | 20 ++++++++- src/ImageSharp/Metadata/IMetaData.cs | 21 +++++++++ src/ImageSharp/Metadata/ImageFrameMetaData.cs | 44 +++++++++++++++++++ src/ImageSharp/Metadata/ImageMetaData.cs | 12 ++++- .../Metadata/ImageFrameMetaDataTests.cs | 26 +++++++++++ .../Metadata/ImageMetaDataTests.cs | 42 ++++++++++++++++++ 11 files changed, 229 insertions(+), 24 deletions(-) create mode 100644 src/ImageSharp/Image/IImageFrame.cs create mode 100644 src/ImageSharp/Metadata/IMetaData.cs create mode 100644 src/ImageSharp/Metadata/ImageFrameMetaData.cs create mode 100644 tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs create mode 100644 tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 9c367c15a6..e6f7b6136f 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -321,12 +321,14 @@ namespace ImageSharp.Formats if (this.previousFrame == null) { - image = this.decodedImage; - this.decodedImage.MetaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. - image.InitPixels(imageWidth, imageHeight); + this.decodedImage.InitPixels(imageWidth, imageHeight); + + this.SetFrameDelay(this.decodedImage.MetaData); + + image = this.decodedImage; } else { @@ -338,6 +340,8 @@ namespace ImageSharp.Formats currentFrame = this.previousFrame.Clone(); + this.SetFrameDelay(currentFrame.MetaData); + image = currentFrame; this.RestoreToBackground(image); @@ -345,11 +349,6 @@ namespace ImageSharp.Formats this.decodedImage.Frames.Add(currentFrame); } - if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) - { - image.FrameDelay = this.graphicsControlExtension.DelayTime; - } - int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment @@ -465,5 +464,17 @@ namespace ImageSharp.Formats this.restoreArea = null; } + + /// + /// Sets the frame delay in the metadata. + /// + /// The meta data. + private void SetFrameDelay(IMetaData metaData) + { + if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) + { + metaData.FrameDelay = this.graphicsControlExtension.DelayTime; + } + } } } \ No newline at end of file diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs index c5923c1a54..80c9ee36bc 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs @@ -229,14 +229,41 @@ namespace ImageSharp.Formats } } + /// + /// Writes the graphics control extension to the stream. + /// + /// The pixel format. + /// The to encode. + /// The stream to write to. + /// The index of the color in the color palette to make transparent. + private void WriteGraphicalControlExtension(Image image, EndianBinaryWriter writer, int transparencyIndex) + where TColor : struct, IPackedPixel, IEquatable + { + this.WriteGraphicalControlExtension(image, image.MetaData, writer, transparencyIndex); + } + + /// + /// Writes the graphics control extension to the stream. + /// + /// The pixel format. + /// The to encode. + /// The stream to write to. + /// The index of the color in the color palette to make transparent. + private void WriteGraphicalControlExtension(ImageFrame imageFrame, EndianBinaryWriter writer, int transparencyIndex) + where TColor : struct, IPackedPixel, IEquatable + { + this.WriteGraphicalControlExtension(imageFrame, imageFrame.MetaData, writer, transparencyIndex); + } + /// /// Writes the graphics control extension to the stream. /// /// The pixel format. /// The to encode. + /// The metadata of the image or frame. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(ImageBase image, EndianBinaryWriter writer, int transparencyIndex) + private void WriteGraphicalControlExtension(ImageBase image, IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) where TColor : struct, IPackedPixel, IEquatable { // TODO: Check transparency logic. @@ -250,7 +277,7 @@ namespace ImageSharp.Formats DisposalMethod = disposalMethod, TransparencyFlag = hasTransparent, TransparencyIndex = transparencyIndex, - DelayTime = image.FrameDelay + DelayTime = metaData.FrameDelay }; // Write the intro. diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs index effbd60063..707fea235d 100644 --- a/src/ImageSharp/Image/IImageBase.cs +++ b/src/ImageSharp/Image/IImageBase.cs @@ -15,14 +15,6 @@ namespace ImageSharp /// Rectangle Bounds { get; } - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, 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. - /// - int FrameDelay { get; set; } - /// /// Gets or sets the maximum allowable width in pixels. /// diff --git a/src/ImageSharp/Image/IImageFrame.cs b/src/ImageSharp/Image/IImageFrame.cs new file mode 100644 index 0000000000..bf3261d93c --- /dev/null +++ b/src/ImageSharp/Image/IImageFrame.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + internal interface IImageFrame : IImageBase + { + /// + /// Gets the meta data of the image. + /// + ImageFrameMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 6fcd7a3b7f..fd82f77308 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -110,9 +110,6 @@ namespace ImageSharp /// public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - /// - public int FrameDelay { get; set; } - /// /// Gets the configuration providing initialization code which allows extending the library. /// @@ -187,7 +184,6 @@ namespace ImageSharp { Debug.Assert(other != null); - this.FrameDelay = other.FrameDelay; this.Configuration = other.Configuration; } diff --git a/src/ImageSharp/Image/ImageFrame{TColor}.cs b/src/ImageSharp/Image/ImageFrame{TColor}.cs index b06b10bc49..02e5b71618 100644 --- a/src/ImageSharp/Image/ImageFrame{TColor}.cs +++ b/src/ImageSharp/Image/ImageFrame{TColor}.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// Represents a single frame in a animation. /// /// The pixel format. - public class ImageFrame : ImageBase + public class ImageFrame : ImageBase, IImageFrame where TColor : struct, IPackedPixel, IEquatable { /// @@ -38,6 +38,11 @@ namespace ImageSharp { } + /// + /// Gets the meta data of the frame. + /// + public ImageFrameMetaData MetaData { get; private set; } = new ImageFrameMetaData(); + /// public override string ToString() { @@ -87,5 +92,18 @@ namespace ImageSharp { return new ImageFrame(this); } + + /// + /// Copies the properties from the other . + /// + /// + /// The other to copy the properties from. + /// + private void CopyProperties(IImageFrame other) + { + base.CopyProperties(other); + + this.MetaData = new ImageFrameMetaData(other.MetaData); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Metadata/IMetaData.cs b/src/ImageSharp/Metadata/IMetaData.cs new file mode 100644 index 0000000000..38fd313493 --- /dev/null +++ b/src/ImageSharp/Metadata/IMetaData.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the metadata of an image frame. + /// + internal interface IMetaData + { + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, 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. + /// + int FrameDelay { get; set; } + } +} diff --git a/src/ImageSharp/Metadata/ImageFrameMetaData.cs b/src/ImageSharp/Metadata/ImageFrameMetaData.cs new file mode 100644 index 0000000000..c2277686f1 --- /dev/null +++ b/src/ImageSharp/Metadata/ImageFrameMetaData.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Diagnostics; + + /// + /// Encapsulates the metadata of an image frame. + /// + public sealed class ImageFrameMetaData : IMetaData + { + /// + /// Initializes a new instance of the class. + /// + internal ImageFrameMetaData() + { + } + + /// + /// Initializes a new instance of the class + /// by making a copy from other metadata. + /// + /// + /// The other to create this instance from. + /// + internal ImageFrameMetaData(ImageFrameMetaData other) + { + Debug.Assert(other != null); + + this.FrameDelay = other.FrameDelay; + } + + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, 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; } + } +} diff --git a/src/ImageSharp/Metadata/ImageMetaData.cs b/src/ImageSharp/Metadata/ImageMetaData.cs index 30bd2c348e..a38899840e 100644 --- a/src/ImageSharp/Metadata/ImageMetaData.cs +++ b/src/ImageSharp/Metadata/ImageMetaData.cs @@ -11,7 +11,7 @@ namespace ImageSharp /// /// Encapsulates the metadata of an image. /// - public sealed class ImageMetaData + public sealed class ImageMetaData : IMetaData { /// /// The default horizontal resolution value (dots per inch) in x direction. @@ -51,6 +51,8 @@ namespace ImageSharp this.HorizontalResolution = other.HorizontalResolution; this.VerticalResolution = other.VerticalResolution; this.Quality = other.Quality; + this.FrameDelay = other.FrameDelay; + this.RepeatCount = other.RepeatCount; foreach (ImageProperty property in other.Properties) { @@ -110,6 +112,14 @@ namespace ImageSharp /// public ExifProfile ExifProfile { get; set; } + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, 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 the list of properties for storing meta information about this image. /// diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs new file mode 100644 index 0000000000..24dd2eac56 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + /// + /// Tests the class. + /// + public class ImageFrameMetaDataTests + { + [Fact] + public void ConstructorImageFrameMetaData() + { + ImageFrameMetaData metaData = new ImageFrameMetaData(); + metaData.FrameDelay = 42; + + ImageFrameMetaData clone = new ImageFrameMetaData(metaData); + + Assert.Equal(42, clone.FrameDelay); + } + } +} diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs new file mode 100644 index 0000000000..9034b88c09 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + /// + /// Tests the class. + /// + public class ImageMetaDataTests + { + [Fact] + public void ConstructorImageMetaData() + { + ImageMetaData metaData = new ImageMetaData(); + + ExifProfile exifProfile = new ExifProfile(); + ImageProperty imageProperty = new ImageProperty("name", "value"); + + metaData.ExifProfile = exifProfile; + metaData.FrameDelay = 42; + metaData.HorizontalResolution = 4; + metaData.VerticalResolution = 2; + metaData.Properties.Add(imageProperty); + metaData.Quality = 24; + metaData.RepeatCount = 1; + + ImageMetaData clone = new ImageMetaData(metaData); + + Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray()); + Assert.Equal(42, clone.FrameDelay); + Assert.Equal(4, clone.HorizontalResolution); + Assert.Equal(2, clone.VerticalResolution); + Assert.Equal(imageProperty, clone.Properties[0]); + Assert.Equal(24, clone.Quality); + Assert.Equal(1, clone.RepeatCount); + } + } +}