Skip to content

Commit d8484da

Browse files
Remove allocations and cleanup.
1 parent a78aab1 commit d8484da

File tree

4 files changed

+92
-122
lines changed

4 files changed

+92
-122
lines changed
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

44
namespace SixLabors.ImageSharp.Formats.Jpeg;
55

66
/// <summary>
7-
/// Contains JPEG comment
7+
/// Represents a JPEG comment
88
/// </summary>
99
public readonly struct JpegComData
1010
{
11-
/// <summary>
12-
/// Converts string to <see cref="JpegComData"/>
13-
/// </summary>
14-
/// <param name="value">The comment string.</param>
15-
/// <returns>The <see cref="JpegComData"/></returns>
16-
public static JpegComData FromString(string value) => new(value.AsMemory());
17-
1811
/// <summary>
1912
/// Initializes a new instance of the <see cref="JpegComData"/> struct.
2013
/// </summary>
21-
/// <param name="value">The comment ReadOnlyMemory of chars.</param>
14+
/// <param name="value">The comment buffer.</param>
2215
public JpegComData(ReadOnlyMemory<char> value)
2316
=> this.Value = value;
2417

18+
/// <summary>
19+
/// Gets the value.
20+
/// </summary>
2521
public ReadOnlyMemory<char> Value { get; }
2622

2723
/// <summary>
28-
/// Converts Value to string
24+
/// Converts string to <see cref="JpegComData"/>
2925
/// </summary>
30-
/// <returns>The comment string.</returns>
26+
/// <param name="value">The comment string.</param>
27+
/// <returns>The <see cref="JpegComData"/></returns>
28+
public static JpegComData FromString(string value) => new(value.AsMemory());
29+
30+
/// <inheritdoc/>
3131
public override string ToString() => this.Value.ToString();
3232
}

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Buffers.Binary;
77
using System.Runtime.CompilerServices;
88
using System.Runtime.InteropServices;
9-
using System.Text;
109
using SixLabors.ImageSharp.Common.Helpers;
1110
using SixLabors.ImageSharp.Formats.Jpeg.Components;
1211
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
@@ -481,6 +480,8 @@ internal void ParseStream(BufferedReadStream stream, SpectralConverter spectralC
481480
break;
482481

483482
case JpegConstants.Markers.APP15:
483+
stream.Skip(markerContentByteSize);
484+
break;
484485
case JpegConstants.Markers.COM:
485486
this.ProcessComMarker(stream, markerContentByteSize);
486487
break;
@@ -523,16 +524,16 @@ public void Dispose()
523524
/// <param name="markerContentByteSize">The remaining bytes in the segment block.</param>
524525
private void ProcessComMarker(BufferedReadStream stream, int markerContentByteSize)
525526
{
526-
char[] temp = new char[markerContentByteSize];
527+
char[] chars = new char[markerContentByteSize];
527528
JpegMetadata metadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance);
528529

529530
for (int i = 0; i < markerContentByteSize; i++)
530531
{
531532
int read = stream.ReadByte();
532-
temp[i] = (char)read;
533+
chars[i] = (char)read;
533534
}
534535

535-
metadata.Comments.Add(new JpegComData(temp));
536+
metadata.Comments.Add(new JpegComData(chars));
536537
}
537538

538539
/// <summary>

src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

Lines changed: 30 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
// Licensed under the Six Labors Split License.
33
#nullable disable
44

5+
using System.Buffers;
56
using System.Buffers.Binary;
6-
using System.Collections;
7-
using System.Text;
87
using SixLabors.ImageSharp.Common.Helpers;
98
using SixLabors.ImageSharp.Formats.Jpeg.Components;
109
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
@@ -27,6 +26,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals
2726
/// </summary>
2827
private static readonly JpegFrameConfig[] FrameConfigs = CreateFrameConfigs();
2928

29+
/// <summary>
30+
/// The current calling encoder.
31+
/// </summary>
3032
private readonly JpegEncoder encoder;
3133

3234
/// <summary>
@@ -92,7 +94,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
9294
this.WriteProfiles(metadata, buffer);
9395

9496
// Write comments
95-
this.WriteComment(jpegMetadata);
97+
this.WriteComments(image.Configuration, jpegMetadata);
9698

9799
// Write the image dimensions.
98100
this.WriteStartOfFrame(image.Width, image.Height, frameConfig, buffer);
@@ -173,67 +175,48 @@ private void WriteJfifApplicationHeader(ImageMetadata meta, Span<byte> buffer)
173175
}
174176

175177
/// <summary>
176-
/// Writes comment
178+
/// Writes the COM tags.
177179
/// </summary>
180+
/// <param name="configuration">The configuration.</param>
178181
/// <param name="metadata">The image metadata.</param>
179-
private void WriteComment(JpegMetadata metadata)
182+
private void WriteComments(Configuration configuration, JpegMetadata metadata)
180183
{
181-
int maxCommentLength = 65533;
182-
183184
if (metadata.Comments.Count == 0)
184185
{
185186
return;
186187
}
187188

188-
// We don't want to modify original metadata
189-
List<JpegComData> comments = new(metadata.Comments);
190-
191-
int totalPayloadLength = 0;
192-
for (int i = 0; i < comments.Count; i++)
189+
const int maxCommentLength = 65533;
190+
using IMemoryOwner<byte> bufferOwner = configuration.MemoryAllocator.Allocate<byte>(maxCommentLength);
191+
Span<byte> buffer = bufferOwner.Memory.Span;
192+
foreach (JpegComData comment in metadata.Comments)
193193
{
194-
JpegComData comment = comments[i];
195-
ReadOnlyMemory<char> currentComment = comment.Value;
196-
197-
if (comment.Value.Length > maxCommentLength)
194+
int totalLength = comment.Value.Length;
195+
if (totalLength == 0)
198196
{
199-
ReadOnlyMemory<char> splitComment =
200-
currentComment.Slice(maxCommentLength, currentComment.Length - maxCommentLength);
201-
comments.Insert(i + 1, new JpegComData(splitComment));
202-
203-
// We don't want to keep the extra bytes
204-
comments[i] = new JpegComData(currentComment.Slice(0, maxCommentLength));
197+
continue;
205198
}
206199

207-
totalPayloadLength += comment.Value.Length + 4;
208-
}
209-
210-
Span<byte> payload = new byte[totalPayloadLength];
211-
int currentCommentStartingIndex = 0;
212-
213-
for (int i = 0; i < comments.Count; i++)
214-
{
215-
ReadOnlyMemory<char> comment = comments[i].Value;
200+
// Loop through and split the comment into multiple comments if the comment length
201+
// is greater than the maximum allowed length.
202+
while (totalLength > 0)
203+
{
204+
int currentLength = Math.Min(totalLength, maxCommentLength);
216205

217-
// Beginning of comment ff fe
218-
payload[currentCommentStartingIndex] = JpegConstants.Markers.XFF;
219-
payload[currentCommentStartingIndex + 1] = JpegConstants.Markers.COM;
206+
// Write the marker header.
207+
this.WriteMarkerHeader(JpegConstants.Markers.COM, currentLength + 2, buffer);
220208

221-
// Write payload size
222-
int comWithoutMarker = comment.Length + 2;
223-
payload[currentCommentStartingIndex + 2] = (byte)((comWithoutMarker >> 8) & 0xFF);
224-
payload[currentCommentStartingIndex + 3] = (byte)(comWithoutMarker & 0xFF);
209+
ReadOnlySpan<char> commentValue = comment.Value.Span.Slice(comment.Value.Length - totalLength, currentLength);
210+
for (int i = 0; i < commentValue.Length; i++)
211+
{
212+
buffer[i] = (byte)commentValue[i];
213+
}
225214

226-
char[] commentChars = comment.ToArray();
227-
for (int j = 0; j < commentChars.Length; j++)
228-
{
229-
// Initial 4 bytes are always reserved
230-
payload[4 + currentCommentStartingIndex + j] = (byte)commentChars[j];
215+
// Write the comment.
216+
this.outputStream.Write(buffer, 0, currentLength);
217+
totalLength -= currentLength;
231218
}
232-
233-
currentCommentStartingIndex += comment.Length + 4;
234219
}
235-
236-
this.outputStream.Write(payload, 0, payload.Length);
237220
}
238221

239222
/// <summary>

0 commit comments

Comments
 (0)