Browse Source

Add com character limit, comment as IList, remove unnecessary extension methods

pull/2641/head
Robert Mutniański 2 years ago
parent
commit
9260be9d29
  1. 43
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  2. 2
      src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
  3. 33
      src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
  4. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  5. 12
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs

43
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -177,39 +177,46 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals
/// <param name="metadata">The image metadata.</param> /// <param name="metadata">The image metadata.</param>
private void WriteComment(JpegMetadata metadata) private void WriteComment(JpegMetadata metadata)
{ {
if (metadata.Comments is { Count: 0 }) int maxCommentLength = 65533;
if (metadata.Comments.Count == 0)
{ {
return; return;
} }
// Length (comment strings lengths) + (comments markers with payload sizes) for (int i = 0; i < metadata.Comments.Count; i++)
int commentsBytes = metadata.Comments.Sum(x => x.Length) + (metadata.Comments.Count * 4);
int commentStart = 0;
Span<byte> commentBuffer = new byte[commentsBytes];
foreach (Memory<char> comment in metadata.Comments)
{ {
int totalComLength = comment.Length + 4; Memory<char> chars = metadata.Comments[i];
if (chars.Length > maxCommentLength)
{
Memory<char> splitComment = chars.Slice(maxCommentLength, chars.Length - maxCommentLength);
metadata.Comments.Insert(i + 1, splitComment);
Span<byte> commentData = commentBuffer.Slice(commentStart, totalComLength); // We don't want to keep the extra bytes
Span<byte> markers = commentData.Slice(0, 2); chars = chars.Slice(0, maxCommentLength);
Span<byte> payloadSize = commentData.Slice(2, 2); }
Span<byte> payload = commentData.Slice(4, comment.Length);
int commentLength = chars.Length + 4;
Span<byte> comment = new byte[commentLength];
Span<byte> markers = comment.Slice(0, 2);
Span<byte> payloadSize = comment.Slice(2, 2);
Span<byte> payload = comment.Slice(4, chars.Length);
// Beginning of comment ff fe // Beginning of comment ff fe
markers[0] = JpegConstants.Markers.XFF; markers[0] = JpegConstants.Markers.XFF;
markers[1] = JpegConstants.Markers.COM; markers[1] = JpegConstants.Markers.COM;
// Write payload size // Write payload size
BinaryPrimitives.WriteInt16BigEndian(payloadSize, (short)(commentData.Length - 2)); int comWithoutMarker = commentLength - 2;
payloadSize[0] = (byte)((comWithoutMarker >> 8) & 0xFF);
payloadSize[1] = (byte)(comWithoutMarker & 0xFF);
Encoding.ASCII.GetBytes(comment.Span, payload); Encoding.ASCII.GetBytes(chars.Span, payload);
// Indicate begin of next comment in buffer this.outputStream.Write(comment, 0, comment.Length);
commentStart += totalComLength;
} }
this.outputStream.Write(commentBuffer, 0, commentBuffer.Length);
} }
/// <summary> /// <summary>

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

@ -106,7 +106,7 @@ public class JpegMetadata : IDeepCloneable
/// <summary> /// <summary>
/// Gets the comments. /// Gets the comments.
/// </summary> /// </summary>
public ICollection<Memory<char>>? Comments { get; } public IList<Memory<char>> Comments { get; }
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new JpegMetadata(this); public IDeepCloneable DeepClone() => new JpegMetadata(this);

33
src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs

@ -18,37 +18,4 @@ public static partial class MetadataExtensions
/// <param name="metadata">The metadata this method extends.</param> /// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="JpegMetadata"/>.</returns> /// <returns>The <see cref="JpegMetadata"/>.</returns>
public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance);
/// <summary>
/// Sets the comment in <see cref="JpegMetadata"/>
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <param name="index">The index of comment to be inserted to.</param>
/// <param name="comment">The comment string.</param>
public static void SetComment(this JpegMetadata metadata, int index, string comment)
{
if (metadata.Comments == null)
{
return;
}
ASCIIEncoding encoding = new();
byte[] bytes = encoding.GetBytes(comment);
List<Memory<char>>? comments = metadata.Comments as List<Memory<char>>;
comments?.Insert(index, encoding.GetChars(bytes));
}
/// <summary>
/// Gets the comments from <see cref="JpegMetadata"/>
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <param name="index">The index of comment.</param>
/// <returns>The IEnumerable string of comments.</returns>
public static string? GetComment(this JpegMetadata metadata, int index) => metadata.Comments?.ElementAtOrDefault(index).ToString();
/// <summary>
/// Clears comments
/// </summary>
/// <param name="metadata">The <see cref="JpegMetadata"/>.</param>
public static void ClearComments(this JpegMetadata metadata) => metadata.Comments?.Clear();
} }

4
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs

@ -434,8 +434,8 @@ public partial class JpegDecoderTests
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance); using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
JpegMetadata metadata = image.Metadata.GetJpegMetadata(); JpegMetadata metadata = image.Metadata.GetJpegMetadata();
Assert.Equal(1, metadata.Comments?.Count); Assert.Equal(1, metadata.Comments.Count);
Assert.Equal(expectedComment, metadata.GetComment(0)); Assert.Equal(expectedComment.ToCharArray(), metadata.Comments.ElementAtOrDefault(0));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToOriginal(provider); image.CompareToOriginal(provider);
} }

12
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs

@ -172,7 +172,7 @@ public partial class JpegEncoderTests
JpegMetadata actual = output.Metadata.GetJpegMetadata(); JpegMetadata actual = output.Metadata.GetJpegMetadata();
Assert.NotEmpty(actual.Comments); Assert.NotEmpty(actual.Comments);
Assert.Equal(1, actual.Comments.Count); Assert.Equal(1, actual.Comments.Count);
Assert.Equal("TEST COMMENT", actual.Comments.ElementAt(0).ToString()); Assert.Equal("TEST COMMENT", actual.Comments.ElementAtOrDefault(0).ToString());
} }
[Fact] [Fact]
@ -184,8 +184,8 @@ public partial class JpegEncoderTests
using var memStream = new MemoryStream(); using var memStream = new MemoryStream();
// act // act
meta.SetComment(0, "First comment"); meta.Comments.Add("First comment".ToCharArray());
meta.SetComment(1, "Second Comment"); meta.Comments.Add("Second Comment".ToCharArray());
input.Save(memStream, JpegEncoder); input.Save(memStream, JpegEncoder);
// assert // assert
@ -193,9 +193,9 @@ public partial class JpegEncoderTests
using Image<Rgba32> output = Image.Load<Rgba32>(memStream); using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
JpegMetadata actual = output.Metadata.GetJpegMetadata(); JpegMetadata actual = output.Metadata.GetJpegMetadata();
Assert.NotEmpty(actual.Comments); Assert.NotEmpty(actual.Comments);
Assert.Equal(2, actual.Comments?.Count); Assert.Equal(2, actual.Comments.Count);
Assert.Equal(meta.Comments?.ElementAt(0).ToString(), actual.Comments?.ElementAt(0).ToString()); Assert.Equal(meta.Comments.ElementAtOrDefault(0).ToString(), actual.Comments.ElementAtOrDefault(0).ToString());
Assert.Equal(meta.Comments?.ElementAt(1).ToString(), actual.Comments?.ElementAt(1).ToString()); Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString());
} }
[Theory] [Theory]

Loading…
Cancel
Save