Skip to content

Commit 8b5703c

Browse files
Synchronize profiles on save.
1 parent a06511f commit 8b5703c

10 files changed

Lines changed: 62 additions & 34 deletions

File tree

src/ImageSharp/Formats/ImageEncoder.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4-
using SixLabors.ImageSharp.Advanced;
54
using SixLabors.ImageSharp.IO;
65
using SixLabors.ImageSharp.PixelFormats;
76

@@ -42,6 +41,8 @@ protected abstract void Encode<TPixel>(Image<TPixel> image, Stream stream, Cance
4241
private void EncodeWithSeekableStream<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
4342
where TPixel : unmanaged, IPixel<TPixel>
4443
{
44+
image.SynchronizeMetadata();
45+
4546
Configuration configuration = image.Configuration;
4647
if (stream.CanSeek)
4748
{
@@ -59,14 +60,16 @@ private void EncodeWithSeekableStream<TPixel>(Image<TPixel> image, Stream stream
5960
private async Task EncodeWithSeekableStreamAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
6061
where TPixel : unmanaged, IPixel<TPixel>
6162
{
63+
image.SynchronizeMetadata();
64+
6265
Configuration configuration = image.Configuration;
6366
if (stream.CanSeek)
6467
{
6568
await DoEncodeAsync(stream).ConfigureAwait(false);
6669
}
6770
else
6871
{
69-
using ChunkedMemoryStream ms = new(configuration.MemoryAllocator);
72+
await using ChunkedMemoryStream ms = new(configuration.MemoryAllocator);
7073
await DoEncodeAsync(ms);
7174
ms.Position = 0;
7275
await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken)

src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -539,17 +539,11 @@ private void WriteIccProfile(IccProfile iccProfile, Span<byte> buffer)
539539
/// <param name="buffer">Temporary buffer.</param>
540540
private void WriteProfiles(ImageMetadata metadata, Span<byte> buffer)
541541
{
542-
if (metadata is null)
543-
{
544-
return;
545-
}
546-
547542
// For compatibility, place the profiles in the following order:
548543
// - APP1 EXIF
549544
// - APP1 XMP
550545
// - APP2 ICC
551546
// - APP13 IPTC
552-
metadata.SyncProfiles();
553547
this.WriteExifProfile(metadata.ExifProfile, buffer);
554548
this.WriteXmpProfile(metadata.XmpProfile, buffer);
555549
this.WriteIccProfile(metadata.IccProfile, buffer);

src/ImageSharp/Formats/Png/PngEncoderCore.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ private void WriteExifChunk(Stream stream, ImageMetadata meta)
776776
return;
777777
}
778778

779-
meta.SyncProfiles();
780779
this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray());
781780
}
782781

src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,6 @@ public WebpVp8X EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool ha
241241
{
242242
// Write bytes from the bit-writer buffer to the stream.
243243
ImageMetadata metadata = image.Metadata;
244-
metadata.SyncProfiles();
245-
246244
ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile;
247245
XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile;
248246

src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,6 @@ public WebpVp8X EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool ha
315315
{
316316
// Write bytes from the bitwriter buffer to the stream.
317317
ImageMetadata metadata = image.Metadata;
318-
metadata.SyncProfiles();
319-
320318
ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile;
321319
XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile;
322320

src/ImageSharp/Image.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,28 @@ public Image<TPixel2> CloneAs<TPixel2>()
157157
public abstract Image<TPixel2> CloneAs<TPixel2>(Configuration configuration)
158158
where TPixel2 : unmanaged, IPixel<TPixel2>;
159159

160+
/// <summary>
161+
/// Synchronizes any embedded metadata profiles with the current image properties.
162+
/// </summary>
163+
public void SynchronizeMetadata()
164+
{
165+
this.Metadata.SynchronizeProfiles();
166+
foreach (ImageFrame frame in this.Frames)
167+
{
168+
frame.Metadata.SynchronizeProfiles();
169+
}
170+
}
171+
172+
/// <summary>
173+
/// Synchronizes any embedded metadata profiles with the current image properties.
174+
/// </summary>
175+
/// <param name="action">A synchronization action to run in addition to the default process.</param>
176+
public void SynchronizeMetadata(Action<Image> action)
177+
{
178+
this.SynchronizeMetadata();
179+
action(this);
180+
}
181+
160182
/// <summary>
161183
/// Update the size of the image after mutation.
162184
/// </summary>

src/ImageSharp/Metadata/ImageFrameMetadata.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,9 @@ public TFormatFrameMetadata CloneFormatMetadata<TFormatMetadata, TFormatFrameMet
131131
where TFormatMetadata : class
132132
where TFormatFrameMetadata : class, IFormatFrameMetadata<TFormatFrameMetadata>
133133
=> ((IDeepCloneable<TFormatFrameMetadata>)this.GetFormatMetadata(key)).DeepClone();
134+
135+
/// <summary>
136+
/// Synchronizes the profiles with the current metadata.
137+
/// </summary>
138+
internal void SynchronizeProfiles() => this.ExifProfile?.Sync(this);
134139
}

src/ImageSharp/Metadata/ImageMetadata.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ public TFormatMetadata CloneFormatMetadata<TFormatMetadata>(IImageFormat<TFormat
219219
/// <inheritdoc/>
220220
public ImageMetadata DeepClone() => new(this);
221221

222+
/// <summary>
223+
/// Synchronizes the profiles with the current metadata.
224+
/// </summary>
225+
internal void SynchronizeProfiles() => this.ExifProfile?.Sync(this);
226+
222227
internal PixelTypeInfo GetDecodedPixelTypeInfo()
223228
{
224229
// None found. Check if we have a decoded format to convert from.
@@ -231,10 +236,4 @@ internal PixelTypeInfo GetDecodedPixelTypeInfo()
231236
// This should never happen.
232237
return default;
233238
}
234-
235-
/// TODO: This should be called on save.
236-
/// <summary>
237-
/// Synchronizes the profiles with the current metadata.
238-
/// </summary>
239-
internal void SyncProfiles() => this.ExifProfile?.Sync(this);
240239
}

src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,17 @@ internal void Sync(ImageMetadata metadata)
298298
this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution);
299299
}
300300

301+
/// <summary>
302+
/// Synchronizes the profiles with the specified metadata.
303+
/// </summary>
304+
/// <param name="metadata">The metadata.</param>
305+
#pragma warning disable CA1822, RCS1163, IDE0060
306+
internal void Sync(ImageFrameMetadata metadata)
307+
#pragma warning restore IDE0060, RCS1163, CA1822
308+
{
309+
// Nothing to do ....YET.
310+
}
311+
301312
private void SyncResolution(ExifTag<Rational> tag, double resolution)
302313
{
303314
if (!this.TryGetValue(tag, out IExifValue<Rational>? value))

tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public class ImageMetadataTests
1616
[Fact]
1717
public void ConstructorImageMetadata()
1818
{
19-
var metaData = new ImageMetadata();
19+
ImageMetadata metaData = new();
2020

21-
var exifProfile = new ExifProfile();
21+
ExifProfile exifProfile = new();
2222

2323
metaData.ExifProfile = exifProfile;
2424
metaData.HorizontalResolution = 4;
@@ -34,7 +34,7 @@ public void ConstructorImageMetadata()
3434
[Fact]
3535
public void CloneIsDeep()
3636
{
37-
var metaData = new ImageMetadata
37+
ImageMetadata metaData = new()
3838
{
3939
ExifProfile = new ExifProfile(),
4040
HorizontalResolution = 4,
@@ -53,7 +53,7 @@ public void CloneIsDeep()
5353
[Fact]
5454
public void HorizontalResolution()
5555
{
56-
var metaData = new ImageMetadata();
56+
ImageMetadata metaData = new();
5757
Assert.Equal(96, metaData.HorizontalResolution);
5858

5959
metaData.HorizontalResolution = 0;
@@ -69,7 +69,7 @@ public void HorizontalResolution()
6969
[Fact]
7070
public void VerticalResolution()
7171
{
72-
var metaData = new ImageMetadata();
72+
ImageMetadata metaData = new();
7373
Assert.Equal(96, metaData.VerticalResolution);
7474

7575
metaData.VerticalResolution = 0;
@@ -85,20 +85,19 @@ public void VerticalResolution()
8585
[Fact]
8686
public void SyncProfiles()
8787
{
88-
var exifProfile = new ExifProfile();
88+
ExifProfile exifProfile = new();
8989
exifProfile.SetValue(ExifTag.XResolution, new Rational(200));
9090
exifProfile.SetValue(ExifTag.YResolution, new Rational(300));
9191

92-
using (var image = new Image<Rgba32>(1, 1))
93-
{
94-
image.Metadata.ExifProfile = exifProfile;
95-
image.Metadata.HorizontalResolution = 400;
96-
image.Metadata.VerticalResolution = 500;
92+
using Image<Rgba32> image = new(1, 1);
93+
image.Metadata.ExifProfile = exifProfile;
94+
image.Metadata.HorizontalResolution = 400;
95+
image.Metadata.VerticalResolution = 500;
9796

98-
image.Metadata.SyncProfiles();
97+
using MemoryStream memoryStream = new();
98+
image.SaveAsBmp(memoryStream);
9999

100-
Assert.Equal(400, image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble());
101-
Assert.Equal(500, image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble());
102-
}
100+
Assert.Equal(400, image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble());
101+
Assert.Equal(500, image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble());
103102
}
104103
}

0 commit comments

Comments
 (0)