diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 98dc80a07b..55335945bc 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
+ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@@ -88,11 +89,6 @@ namespace ImageSharp.Formats
///
private bool isExif;
- ///
- /// Whether the image has an ICC header
- ///
- private bool isIcc;
-
///
/// The vertical resolution. Calculated if the image has a JFIF header.
///
@@ -997,11 +993,11 @@ namespace ImageSharp.Formats
identifier[10] == 'E' &&
identifier[11] == '\0')
{
- this.isIcc = true;
remaining -= Icclength;
byte[] profile = new byte[remaining];
this.InputProcessor.ReadFull(profile, 0, remaining);
- metadata.IccProfile = new IccProfile(profile);
+
+ metadata.IccProfiles.Add(new IccProfile(profile));
}
}
@@ -1021,8 +1017,11 @@ namespace ImageSharp.Formats
remaining -= 13;
// TODO: We should be using constants for this.
- this.isJfif = this.Temp[0] == 'J' && this.Temp[1] == 'F' && this.Temp[2] == 'I' && this.Temp[3] == 'F'
- && this.Temp[4] == '\x00';
+ this.isJfif = this.Temp[0] == 'J' &&
+ this.Temp[1] == 'F' &&
+ this.Temp[2] == 'I' &&
+ this.Temp[3] == 'F' &&
+ this.Temp[4] == '\x00';
if (this.isJfif)
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 0ce59c6dec..4dddd25ed7 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -6,7 +6,9 @@
namespace ImageSharp.Formats
{
using System.Buffers;
+ using System.Collections.Generic;
using System.IO;
+ using System.Linq;
using System.Runtime.CompilerServices;
using ImageSharp.Formats.Jpg;
using ImageSharp.Formats.Jpg.Components;
@@ -109,7 +111,7 @@ namespace ImageSharp.Formats
///
/// A scratch buffer to reduce allocations.
///
- private readonly byte[] buffer = new byte[16];
+ private readonly byte[] buffer = new byte[20];
///
/// A buffer for reducing the number of stream writes when emitting Huffman tables. 64 seems to be enough.
@@ -514,19 +516,17 @@ namespace ImageSharp.Formats
this.buffer[12] = 0x01; // versionlo
this.buffer[13] = 0x01; // xyunits as dpi
- // No thumbnail
- this.buffer[14] = 0x00; // Thumbnail width
- this.buffer[15] = 0x00; // Thumbnail height
-
- this.outputStream.Write(this.buffer, 0, 16);
-
// Resolution. Big Endian
- this.buffer[0] = (byte)(horizontalResolution >> 8);
- this.buffer[1] = (byte)horizontalResolution;
- this.buffer[2] = (byte)(verticalResolution >> 8);
- this.buffer[3] = (byte)verticalResolution;
+ this.buffer[14] = (byte)(horizontalResolution >> 8);
+ this.buffer[15] = (byte)horizontalResolution;
+ this.buffer[16] = (byte)(verticalResolution >> 8);
+ this.buffer[17] = (byte)verticalResolution;
- this.outputStream.Write(this.buffer, 0, 4);
+ // No thumbnail
+ this.buffer[18] = 0x00; // Thumbnail width
+ this.buffer[19] = 0x00; // Thumbnail height
+
+ this.outputStream.Write(this.buffer, 0, 20);
}
///
@@ -674,7 +674,7 @@ namespace ImageSharp.Formats
///
/// Thrown if the EXIF profile size exceeds the limit
///
- private void WriteProfile(ExifProfile exifProfile)
+ private void WriteExifProfile(ExifProfile exifProfile)
{
const int Max = 65533;
byte[] data = exifProfile?.ToByteArray();
@@ -699,6 +699,66 @@ namespace ImageSharp.Formats
this.outputStream.Write(data, 0, data.Length);
}
+ ///
+ /// Writes the ICC profiles.
+ ///
+ /// The list of ICC profiles.
+ ///
+ /// Thrown if any of the ICC profiles size exceeds the limit
+ ///
+ private void WriteICCProfiles(IList iccProfiles)
+ {
+ // Just incase someone set the value to null by accident.
+ if (iccProfiles == null || !iccProfiles.Any())
+ {
+ return;
+ }
+
+ const int Max = 65533;
+ int count = iccProfiles.Count;
+
+ for (int i = 1; i <= count; i++)
+ {
+ byte[] data = iccProfiles[i - 1]?.ToByteArray();
+
+ if (data == null || data.Length == 0)
+ {
+ continue;
+ }
+
+ if (data.Length > Max)
+ {
+ throw new ImageFormatException($"ICC profile size exceeds limit. nameof{Max}");
+ }
+
+ this.buffer[0] = JpegConstants.Markers.XFF;
+ this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker
+ int length = data.Length + 16;
+ this.buffer[2] = (byte)((length >> 8) & 0xFF);
+ this.buffer[3] = (byte)(length & 0xFF);
+
+ this.outputStream.Write(this.buffer, 0, 4);
+
+ this.buffer[0] = (byte)'I';
+ this.buffer[1] = (byte)'C';
+ this.buffer[2] = (byte)'C';
+ this.buffer[3] = (byte)'_';
+ this.buffer[4] = (byte)'P';
+ this.buffer[5] = (byte)'R';
+ this.buffer[6] = (byte)'O';
+ this.buffer[7] = (byte)'F';
+ this.buffer[8] = (byte)'I';
+ this.buffer[9] = (byte)'L';
+ this.buffer[10] = (byte)'E';
+ this.buffer[11] = 0x00;
+ this.buffer[12] = (byte)i; // The position within the collection.
+ this.buffer[13] = (byte)count; // The total number of profiles.
+
+ this.outputStream.Write(this.buffer, 0, 14);
+ this.outputStream.Write(data, 0, data.Length);
+ }
+ }
+
///
/// Writes the metadata profiles to the image.
///
@@ -713,7 +773,8 @@ namespace ImageSharp.Formats
}
image.MetaData.SyncProfiles();
- this.WriteProfile(image.MetaData.ExifProfile);
+ this.WriteExifProfile(image.MetaData.ExifProfile);
+ this.WriteICCProfiles(image.MetaData.IccProfiles);
}
///
diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs
index c8f3ccc6c7..08ccc59fa8 100644
--- a/src/ImageSharp/MetaData/ImageMetaData.cs
+++ b/src/ImageSharp/MetaData/ImageMetaData.cs
@@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.Collections.Generic;
+ using System.Linq;
///
/// Encapsulates the metadata of an image.
@@ -67,6 +68,15 @@ namespace ImageSharp
{
this.ExifProfile = null;
}
+
+ if (other.IccProfiles != null && other.IccProfiles.Any())
+ {
+ this.IccProfiles = new List(other.IccProfiles);
+ }
+ else
+ {
+ this.ExifProfile = null;
+ }
}
///
@@ -83,10 +93,10 @@ namespace ImageSharp
set
{
- if (value > 0)
- {
- this.horizontalResolution = value;
- }
+ if (value > 0)
+ {
+ this.horizontalResolution = value;
+ }
}
}
@@ -117,9 +127,9 @@ namespace ImageSharp
public ExifProfile ExifProfile { get; set; }
///
- /// Gets or sets the ICC profile.
+ /// Gets or sets the list of ICC profiles.
///
- public IccProfile IccProfile { get; set; }
+ public IList IccProfiles { get; set; } = new List();
///
/// Gets or sets the frame delay for animated images.
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs
index abe91e4817..d515ee7263 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs
@@ -12,7 +12,7 @@ namespace ImageSharp
///
/// Provides methods to write ICC data types
///
- internal sealed partial class IccDataWriter
+ internal sealed partial class IccDataWriter : IDisposable
{
private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII");
@@ -22,6 +22,11 @@ namespace ImageSharp
///
private readonly MemoryStream dataStream;
+ ///
+ /// To detect redundant calls
+ ///
+ private bool isDisposed = false;
+
///
/// Initializes a new instance of the class.
///
@@ -167,6 +172,12 @@ namespace ImageSharp
return this.WriteEmpty(p >= 4 ? 0 : p);
}
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+ }
+
///
/// Writes given bytes from pointer
///
@@ -228,5 +239,18 @@ namespace ImageSharp
return count;
}
+
+ private void Dispose(bool disposing)
+ {
+ if (!this.isDisposed)
+ {
+ if (disposing)
+ {
+ this.dataStream?.Dispose();
+ }
+
+ this.isDisposed = true;
+ }
+ }
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
index b23c8b0689..2701ffcb11 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
@@ -124,6 +124,16 @@ namespace ImageSharp
#endif
+ ///
+ /// Converts this instance to a byte array.
+ ///
+ /// The
+ public byte[] ToByteArray()
+ {
+ IccWriter writer = new IccWriter();
+ return writer.Write(this);
+ }
+
private void InitializeHeader()
{
if (this.header != null)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs
index 54a1cb21ac..dcf2f056fe 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs
@@ -22,11 +22,13 @@ namespace ImageSharp
{
Guard.NotNull(profile, nameof(profile));
- IccDataWriter writer = new IccDataWriter();
- IccTagTableEntry[] tagTable = this.WriteTagData(writer, profile.Entries);
- this.WriteTagTable(writer, tagTable);
- this.WriteHeader(writer, profile.Header);
- return writer.GetData();
+ using (IccDataWriter writer = new IccDataWriter())
+ {
+ IccTagTableEntry[] tagTable = this.WriteTagData(writer, profile.Entries);
+ this.WriteTagTable(writer, tagTable);
+ this.WriteHeader(writer, profile.Header);
+ return writer.GetData();
+ }
}
private void WriteHeader(IccDataWriter writer, IccProfileHeader header)