|
|
|
@ -213,6 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg |
|
|
|
// Write the Start Of Image marker.
|
|
|
|
this.WriteApplicationHeader((short)image.MetaData.HorizontalResolution, (short)image.MetaData.VerticalResolution); |
|
|
|
|
|
|
|
// Write Exif and ICC profiles
|
|
|
|
this.WriteProfiles(image); |
|
|
|
|
|
|
|
// Write the quantization tables.
|
|
|
|
@ -608,27 +609,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg |
|
|
|
/// </exception>
|
|
|
|
private void WriteExifProfile(ExifProfile exifProfile) |
|
|
|
{ |
|
|
|
const int Max = 65533; |
|
|
|
const int MaxBytesApp1 = 65533; |
|
|
|
const int MaxBytesWithExifId = 65527; |
|
|
|
byte[] data = exifProfile?.ToByteArray(ProfileResolver.ExifMarker); |
|
|
|
if (data == null || data.Length == 0) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (data.Length > Max) |
|
|
|
int remaining = data.Length; |
|
|
|
int bytesToWrite = remaining > MaxBytesApp1 ? MaxBytesApp1 : remaining; |
|
|
|
int app1Length = bytesToWrite + 2; |
|
|
|
|
|
|
|
// write the app1 header
|
|
|
|
this.WriteApp1Header(app1Length); |
|
|
|
|
|
|
|
// write the exif data
|
|
|
|
this.outputStream.Write(data, 0, bytesToWrite); |
|
|
|
remaining -= bytesToWrite; |
|
|
|
|
|
|
|
// if the exif data exceeds 64K, write it in multiple APP1 Markers
|
|
|
|
for (int idx = MaxBytesApp1; idx < data.Length; idx += MaxBytesWithExifId) |
|
|
|
{ |
|
|
|
throw new ImageFormatException($"Exif profile size exceeds limit of {Max} bytes."); |
|
|
|
} |
|
|
|
bytesToWrite = remaining > MaxBytesWithExifId ? MaxBytesWithExifId : remaining; |
|
|
|
app1Length = bytesToWrite + 2 + 6; |
|
|
|
|
|
|
|
// write the app1 header
|
|
|
|
this.WriteApp1Header(app1Length); |
|
|
|
|
|
|
|
// write Exif00 marker
|
|
|
|
ProfileResolver.ExifMarker.AsSpan().CopyTo(this.buffer.AsSpan()); |
|
|
|
this.outputStream.Write(this.buffer, 0, 6); |
|
|
|
|
|
|
|
int length = data.Length + 2; |
|
|
|
// write the exif data
|
|
|
|
this.outputStream.Write(data, idx, bytesToWrite); |
|
|
|
|
|
|
|
remaining -= bytesToWrite; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Writes the App1 header.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="app1Length">The length of the data the app1 marker contains</param>
|
|
|
|
private void WriteApp1Header(int app1Length) |
|
|
|
{ |
|
|
|
this.buffer[0] = JpegConstants.Markers.XFF; |
|
|
|
this.buffer[1] = JpegConstants.Markers.APP1; // Application Marker
|
|
|
|
this.buffer[2] = (byte)((length >> 8) & 0xFF); |
|
|
|
this.buffer[3] = (byte)(length & 0xFF); |
|
|
|
this.buffer[2] = (byte)((app1Length >> 8) & 0xFF); |
|
|
|
this.buffer[3] = (byte)(app1Length & 0xFF); |
|
|
|
|
|
|
|
this.outputStream.Write(this.buffer, 0, 4); |
|
|
|
this.outputStream.Write(data, 0, data.Length); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|