diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 1b2b4cbb1..4dc920207 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -177,39 +177,46 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// The image metadata. private void WriteComment(JpegMetadata metadata) { - if (metadata.Comments is { Count: 0 }) + int maxCommentLength = 65533; + + if (metadata.Comments.Count == 0) { return; } - // Length (comment strings lengths) + (comments markers with payload sizes) - int commentsBytes = metadata.Comments.Sum(x => x.Length) + (metadata.Comments.Count * 4); - int commentStart = 0; - Span commentBuffer = new byte[commentsBytes]; - - foreach (Memory comment in metadata.Comments) + for (int i = 0; i < metadata.Comments.Count; i++) { - int totalComLength = comment.Length + 4; + Memory chars = metadata.Comments[i]; + + if (chars.Length > maxCommentLength) + { + Memory splitComment = chars.Slice(maxCommentLength, chars.Length - maxCommentLength); + metadata.Comments.Insert(i + 1, splitComment); - Span commentData = commentBuffer.Slice(commentStart, totalComLength); - Span markers = commentData.Slice(0, 2); - Span payloadSize = commentData.Slice(2, 2); - Span payload = commentData.Slice(4, comment.Length); + // We don't want to keep the extra bytes + chars = chars.Slice(0, maxCommentLength); + } + + int commentLength = chars.Length + 4; + + Span comment = new byte[commentLength]; + Span markers = comment.Slice(0, 2); + Span payloadSize = comment.Slice(2, 2); + Span payload = comment.Slice(4, chars.Length); // Beginning of comment ff fe markers[0] = JpegConstants.Markers.XFF; markers[1] = JpegConstants.Markers.COM; // 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 - commentStart += totalComLength; + this.outputStream.Write(comment, 0, comment.Length); } - - this.outputStream.Write(commentBuffer, 0, commentBuffer.Length); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 61fe3b214..bf758dfd0 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -106,7 +106,7 @@ public class JpegMetadata : IDeepCloneable /// /// Gets the comments. /// - public ICollection>? Comments { get; } + public IList> Comments { get; } /// public IDeepCloneable DeepClone() => new JpegMetadata(this); diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs index 0c66fcbdd..7330e74b7 100644 --- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs @@ -18,37 +18,4 @@ public static partial class MetadataExtensions /// The metadata this method extends. /// The . public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); - - /// - /// Sets the comment in - /// - /// The metadata this method extends. - /// The index of comment to be inserted to. - /// The comment string. - 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>? comments = metadata.Comments as List>; - comments?.Insert(index, encoding.GetChars(bytes)); - } - - /// - /// Gets the comments from - /// - /// The metadata this method extends. - /// The index of comment. - /// The IEnumerable string of comments. - public static string? GetComment(this JpegMetadata metadata, int index) => metadata.Comments?.ElementAtOrDefault(index).ToString(); - - /// - /// Clears comments - /// - /// The . - public static void ClearComments(this JpegMetadata metadata) => metadata.Comments?.Clear(); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index fb37a956d..369e71abf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -434,8 +434,8 @@ public partial class JpegDecoderTests using Image image = provider.GetImage(JpegDecoder.Instance); JpegMetadata metadata = image.Metadata.GetJpegMetadata(); - Assert.Equal(1, metadata.Comments?.Count); - Assert.Equal(expectedComment, metadata.GetComment(0)); + Assert.Equal(1, metadata.Comments.Count); + Assert.Equal(expectedComment.ToCharArray(), metadata.Comments.ElementAtOrDefault(0)); image.DebugSave(provider); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index bd68eaf20..8cc64acea 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -172,7 +172,7 @@ public partial class JpegEncoderTests JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); 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] @@ -184,8 +184,8 @@ public partial class JpegEncoderTests using var memStream = new MemoryStream(); // act - meta.SetComment(0, "First comment"); - meta.SetComment(1, "Second Comment"); + meta.Comments.Add("First comment".ToCharArray()); + meta.Comments.Add("Second Comment".ToCharArray()); input.Save(memStream, JpegEncoder); // assert @@ -193,9 +193,9 @@ public partial class JpegEncoderTests using Image output = Image.Load(memStream); JpegMetadata actual = output.Metadata.GetJpegMetadata(); Assert.NotEmpty(actual.Comments); - Assert.Equal(2, actual.Comments?.Count); - Assert.Equal(meta.Comments?.ElementAt(0).ToString(), actual.Comments?.ElementAt(0).ToString()); - Assert.Equal(meta.Comments?.ElementAt(1).ToString(), actual.Comments?.ElementAt(1).ToString()); + Assert.Equal(2, actual.Comments.Count); + Assert.Equal(meta.Comments.ElementAtOrDefault(0).ToString(), actual.Comments.ElementAtOrDefault(0).ToString()); + Assert.Equal(meta.Comments.ElementAtOrDefault(1).ToString(), actual.Comments.ElementAtOrDefault(1).ToString()); } [Theory]