Browse Source

Can now save some ICC profiles

CMYK Image throws an error when trying to parse/write entries
af/merge-core
James Jackson-South 9 years ago
parent
commit
a7df079d81
  1. 17
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  2. 89
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  3. 22
      src/ImageSharp/MetaData/ImageMetaData.cs
  4. 26
      src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs
  5. 10
      src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
  6. 12
      src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs

17
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
/// </summary>
private bool isExif;
/// <summary>
/// Whether the image has an ICC header
/// </summary>
private bool isIcc;
/// <summary>
/// The vertical resolution. Calculated if the image has a JFIF header.
/// </summary>
@ -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)
{

89
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
/// <summary>
/// A scratch buffer to reduce allocations.
/// </summary>
private readonly byte[] buffer = new byte[16];
private readonly byte[] buffer = new byte[20];
/// <summary>
/// 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);
}
/// <summary>
@ -674,7 +674,7 @@ namespace ImageSharp.Formats
/// <exception cref="ImageFormatException">
/// Thrown if the EXIF profile size exceeds the limit
/// </exception>
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);
}
/// <summary>
/// Writes the ICC profiles.
/// </summary>
/// <param name="iccProfiles">The list of ICC profiles.</param>
/// <exception cref="ImageFormatException">
/// Thrown if any of the ICC profiles size exceeds the limit
/// </exception>
private void WriteICCProfiles(IList<IccProfile> 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);
}
}
/// <summary>
/// Writes the metadata profiles to the image.
/// </summary>
@ -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);
}
/// <summary>

22
src/ImageSharp/MetaData/ImageMetaData.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// 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<IccProfile>(other.IccProfiles);
}
else
{
this.ExifProfile = null;
}
}
/// <summary>
@ -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; }
/// <summary>
/// Gets or sets the ICC profile.
/// Gets or sets the list of ICC profiles.
/// </summary>
public IccProfile IccProfile { get; set; }
public IList<IccProfile> IccProfiles { get; set; } = new List<IccProfile>();
/// <summary>
/// Gets or sets the frame delay for animated images.

26
src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs

@ -12,7 +12,7 @@ namespace ImageSharp
/// <summary>
/// Provides methods to write ICC data types
/// </summary>
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
/// </summary>
private readonly MemoryStream dataStream;
/// <summary>
/// To detect redundant calls
/// </summary>
private bool isDisposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="IccDataWriter"/> class.
/// </summary>
@ -167,6 +172,12 @@ namespace ImageSharp
return this.WriteEmpty(p >= 4 ? 0 : p);
}
/// <inheritdoc/>
public void Dispose()
{
this.Dispose(true);
}
/// <summary>
/// Writes given bytes from pointer
/// </summary>
@ -228,5 +239,18 @@ namespace ImageSharp
return count;
}
private void Dispose(bool disposing)
{
if (!this.isDisposed)
{
if (disposing)
{
this.dataStream?.Dispose();
}
this.isDisposed = true;
}
}
}
}

10
src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs

@ -124,6 +124,16 @@ namespace ImageSharp
#endif
/// <summary>
/// Converts this instance to a byte array.
/// </summary>
/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray()
{
IccWriter writer = new IccWriter();
return writer.Write(this);
}
private void InitializeHeader()
{
if (this.header != null)

12
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)

Loading…
Cancel
Save