Browse Source

Rename properties and add metadata tests

pull/2511/head
James Jackson-South 3 years ago
parent
commit
14a95a8ed6
  1. 2
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 6
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 18
      src/ImageSharp/Formats/Png/PngFrameMetadata.cs
  4. 6
      src/ImageSharp/Formats/Png/PngMetadata.cs
  5. 17
      src/ImageSharp/Primitives/Rational.cs
  6. 17
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  7. 35
      tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs
  8. 13
      tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs

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

@ -1258,7 +1258,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
{
this.animationControl = AnimationControl.Parse(data);
pngMetadata.NumberPlays = this.animationControl.NumberPlays;
pngMetadata.RepeatCount = this.animationControl.NumberPlays;
}
/// <summary>

6
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -176,7 +176,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
if (image.Frames.Count > 1)
{
this.WriteAnimationControlChunk(stream, image.Frames.Count, pngMetadata.NumberPlays);
this.WriteAnimationControlChunk(stream, image.Frames.Count, pngMetadata.RepeatCount);
// TODO: We should attempt to optimize the output by clipping the indexed result to
// non-transparent bounds. That way we can assign frame control bounds and encode
@ -996,8 +996,8 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
height: (uint)imageFrame.Height,
xOffset: 0,
yOffset: 0,
delayNumerator: frameMetadata.DelayNumerator,
delayDenominator: frameMetadata.DelayDenominator,
delayNumerator: (ushort)frameMetadata.FrameDelay.Numerator,
delayDenominator: (ushort)frameMetadata.FrameDelay.Denominator,
disposeOperation: frameMetadata.DisposalMethod,
blendOperation: frameMetadata.BlendMethod);

18
src/ImageSharp/Formats/Png/PngFrameMetadata.cs

@ -23,21 +23,18 @@ public class PngFrameMetadata : IDeepCloneable
/// <param name="other">The metadata to create an instance from.</param>
private PngFrameMetadata(PngFrameMetadata other)
{
this.DelayNumerator = other.DelayNumerator;
this.DelayDenominator = other.DelayDenominator;
this.FrameDelay = other.FrameDelay;
this.DisposalMethod = other.DisposalMethod;
this.BlendMethod = other.BlendMethod;
}
/// <summary>
/// Gets or sets the frame delay fraction numerator
/// Gets or sets the frame delay for animated images.
/// If not 0, when utilized in Png 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 ushort DelayNumerator { get; set; }
/// <summary>
/// Gets or sets the frame delay fraction denominator
/// </summary>
public ushort DelayDenominator { get; set; }
public Rational FrameDelay { get; set; }
/// <summary>
/// Gets or sets the type of frame area disposal to be done after rendering this frame
@ -55,8 +52,7 @@ public class PngFrameMetadata : IDeepCloneable
/// <param name="frameControl">The chunk to create an instance from.</param>
internal void FromChunk(in FrameControl frameControl)
{
this.DelayNumerator = frameControl.DelayNumerator;
this.DelayDenominator = frameControl.DelayDenominator;
this.FrameDelay = new Rational(frameControl.DelayNumerator, frameControl.DelayDenominator);
this.DisposalMethod = frameControl.DisposeOperation;
this.BlendMethod = frameControl.BlendOperation;
}

6
src/ImageSharp/Formats/Png/PngMetadata.cs

@ -29,7 +29,7 @@ public class PngMetadata : IDeepCloneable
this.Gamma = other.Gamma;
this.InterlaceMethod = other.InterlaceMethod;
this.TransparentColor = other.TransparentColor;
this.NumberPlays = other.NumberPlays;
this.RepeatCount = other.RepeatCount;
if (other.ColorTable?.Length > 0)
{
@ -80,9 +80,9 @@ public class PngMetadata : IDeepCloneable
public IList<PngTextData> TextData { get; set; } = new List<PngTextData>();
/// <summary>
/// Gets or sets the number of times to loop this APNG. 0 indicates infinite looping. TODO: RepeatCount!!
/// Gets or sets the number of times to loop this APNG. 0 indicates infinite looping.
/// </summary>
public int NumberPlays { get; set; }
public int RepeatCount { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new PngMetadata(this);

17
src/ImageSharp/Primitives/Rational.cs

@ -70,7 +70,7 @@ public readonly struct Rational : IEquatable<Rational>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public Rational(double value, bool bestPrecision)
{
var rational = LongRational.FromDouble(Math.Abs(value), bestPrecision);
LongRational rational = LongRational.FromDouble(Math.Abs(value), bestPrecision);
this.Numerator = (uint)rational.Numerator;
this.Denominator = (uint)rational.Denominator;
@ -109,7 +109,7 @@ public readonly struct Rational : IEquatable<Rational>
/// <returns>
/// The <see cref="Rational"/>.
/// </returns>
public static Rational FromDouble(double value) => new Rational(value, false);
public static Rational FromDouble(double value) => new(value, false);
/// <summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
@ -119,24 +119,19 @@ public readonly struct Rational : IEquatable<Rational>
/// <returns>
/// The <see cref="Rational"/>.
/// </returns>
public static Rational FromDouble(double value, bool bestPrecision) => new Rational(value, bestPrecision);
public static Rational FromDouble(double value, bool bestPrecision) => new(value, bestPrecision);
/// <inheritdoc/>
public override bool Equals(object? obj) => obj is Rational other && this.Equals(other);
/// <inheritdoc/>
public bool Equals(Rational other)
{
var left = new LongRational(this.Numerator, this.Denominator);
var right = new LongRational(other.Numerator, other.Denominator);
return left.Equals(right);
}
=> this.Numerator == other.Numerator && this.Denominator == other.Denominator;
/// <inheritdoc/>
public override int GetHashCode()
{
var self = new LongRational(this.Numerator, this.Denominator);
LongRational self = new(this.Numerator, this.Denominator);
return self.GetHashCode();
}
@ -169,7 +164,7 @@ public readonly struct Rational : IEquatable<Rational>
/// <returns>The <see cref="string"/></returns>
public string ToString(IFormatProvider provider)
{
var rational = new LongRational(this.Numerator, this.Denominator);
LongRational rational = new(this.Numerator, this.Denominator);
return rational.ToString(provider);
}
}

17
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -457,8 +457,23 @@ public partial class PngEncoderTests
using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
ImageComparer.Exact.VerifySimilarity(output, image);
// TODO: Additional assertations regarding metadata.
Assert.Equal(5, image.Frames.Count);
Assert.Equal(image.Frames.Count, output.Frames.Count);
PngMetadata originalMetadata = image.Metadata.GetPngMetadata();
PngMetadata outputMetadata = output.Metadata.GetPngMetadata();
Assert.Equal(originalMetadata.RepeatCount, outputMetadata.RepeatCount);
for (int i = 0; i < image.Frames.Count; i++)
{
PngFrameMetadata originalFrameMetadata = image.Frames[i].Metadata.GetPngFrameMetadata();
PngFrameMetadata outputFrameMetadata = output.Frames[i].Metadata.GetPngFrameMetadata();
Assert.Equal(originalFrameMetadata.FrameDelay, outputFrameMetadata.FrameDelay);
Assert.Equal(originalFrameMetadata.BlendMethod, outputFrameMetadata.BlendMethod);
Assert.Equal(originalFrameMetadata.DisposalMethod, outputFrameMetadata.DisposalMethod);
}
}
[Theory]

35
tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Png;
namespace SixLabors.ImageSharp.Tests.Formats.Png;
[Trait("Format", "Png")]
public class PngFrameMetadataTests
{
[Fact]
public void CloneIsDeep()
{
PngFrameMetadata meta = new()
{
FrameDelay = new(1, 0),
DisposalMethod = PngDisposalMethod.Background,
BlendMethod = PngBlendMethod.Over,
};
PngFrameMetadata clone = (PngFrameMetadata)meta.DeepClone();
Assert.True(meta.FrameDelay.Equals(clone.FrameDelay));
Assert.True(meta.DisposalMethod.Equals(clone.DisposalMethod));
Assert.True(meta.BlendMethod.Equals(clone.BlendMethod));
clone.FrameDelay = new(2, 1);
clone.DisposalMethod = PngDisposalMethod.Previous;
clone.BlendMethod = PngBlendMethod.Source;
Assert.False(meta.FrameDelay.Equals(clone.FrameDelay));
Assert.False(meta.DisposalMethod.Equals(clone.DisposalMethod));
Assert.False(meta.BlendMethod.Equals(clone.BlendMethod));
}
}

13
tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs

@ -31,15 +31,25 @@ public class PngMetadataTests
ColorType = PngColorType.GrayscaleWithAlpha,
InterlaceMethod = PngInterlaceMode.Adam7,
Gamma = 2,
TextData = new List<PngTextData> { new PngTextData("name", "value", "foo", "bar") }
TextData = new List<PngTextData> { new PngTextData("name", "value", "foo", "bar") },
RepeatCount = 123
};
PngMetadata clone = (PngMetadata)meta.DeepClone();
Assert.True(meta.BitDepth == clone.BitDepth);
Assert.True(meta.ColorType == clone.ColorType);
Assert.True(meta.InterlaceMethod == clone.InterlaceMethod);
Assert.True(meta.Gamma.Equals(clone.Gamma));
Assert.False(meta.TextData.Equals(clone.TextData));
Assert.True(meta.TextData.SequenceEqual(clone.TextData));
Assert.True(meta.RepeatCount == clone.RepeatCount);
clone.BitDepth = PngBitDepth.Bit2;
clone.ColorType = PngColorType.Palette;
clone.InterlaceMethod = PngInterlaceMode.None;
clone.Gamma = 1;
clone.RepeatCount = 321;
Assert.False(meta.BitDepth == clone.BitDepth);
Assert.False(meta.ColorType == clone.ColorType);
@ -47,6 +57,7 @@ public class PngMetadataTests
Assert.False(meta.Gamma.Equals(clone.Gamma));
Assert.False(meta.TextData.Equals(clone.TextData));
Assert.True(meta.TextData.SequenceEqual(clone.TextData));
Assert.False(meta.RepeatCount == clone.RepeatCount);
}
[Theory]

Loading…
Cancel
Save