mirror of https://github.com/SixLabors/ImageSharp
18 changed files with 3816 additions and 3681 deletions
@ -0,0 +1,221 @@ |
|||
// <copyright file="IccDataReader.Curves.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads a <see cref="IccOneDimensionalCurve"/>
|
|||
/// </summary>
|
|||
/// <returns>The read curve</returns>
|
|||
public IccOneDimensionalCurve ReadOneDimensionalCurve() |
|||
{ |
|||
ushort segmentCount = this.ReadUInt16(); |
|||
this.AddIndex(2); // 2 bytes reserved
|
|||
float[] breakPoints = new float[segmentCount - 1]; |
|||
for (int i = 0; i < breakPoints.Length; i++) |
|||
{ |
|||
breakPoints[i] = this.ReadSingle(); |
|||
} |
|||
|
|||
IccCurveSegment[] segments = new IccCurveSegment[segmentCount]; |
|||
for (int i = 0; i < segmentCount; i++) |
|||
{ |
|||
segments[i] = this.ReadCurveSegment(); |
|||
} |
|||
|
|||
return new IccOneDimensionalCurve(breakPoints, segments); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccResponseCurve"/>
|
|||
/// </summary>
|
|||
/// <param name="channelCount">The number of channels</param>
|
|||
/// <returns>The read curve</returns>
|
|||
public IccResponseCurve ReadResponseCurve(int channelCount) |
|||
{ |
|||
IccCurveMeasurementEncodings type = (IccCurveMeasurementEncodings)this.ReadUInt32(); |
|||
uint[] measurment = new uint[channelCount]; |
|||
for (int i = 0; i < channelCount; i++) |
|||
{ |
|||
measurment[i] = this.ReadUInt32(); |
|||
} |
|||
|
|||
Vector3[] xyzValues = new Vector3[channelCount]; |
|||
for (int i = 0; i < channelCount; i++) |
|||
{ |
|||
xyzValues[i] = this.ReadXyzNumber(); |
|||
} |
|||
|
|||
IccResponseNumber[][] response = new IccResponseNumber[channelCount][]; |
|||
for (int i = 0; i < channelCount; i++) |
|||
{ |
|||
response[i] = new IccResponseNumber[measurment[i]]; |
|||
for (uint j = 0; j < measurment[i]; j++) |
|||
{ |
|||
response[i][j] = this.ReadResponseNumber(); |
|||
} |
|||
} |
|||
|
|||
return new IccResponseCurve(type, xyzValues, response); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccParametricCurve"/>
|
|||
/// </summary>
|
|||
/// <returns>The read curve</returns>
|
|||
public IccParametricCurve ReadParametricCurve() |
|||
{ |
|||
ushort type = this.ReadUInt16(); |
|||
this.AddIndex(2); // 2 bytes reserved
|
|||
double gamma, a, b, c, d, e, f; |
|||
gamma = a = b = c = d = e = f = 0; |
|||
|
|||
if (type >= 0 && type <= 4) |
|||
{ |
|||
gamma = this.ReadFix16(); |
|||
} |
|||
|
|||
if (type > 0 && type <= 4) |
|||
{ |
|||
a = this.ReadFix16(); |
|||
b = this.ReadFix16(); |
|||
} |
|||
|
|||
if (type > 1 && type <= 4) |
|||
{ |
|||
c = this.ReadFix16(); |
|||
} |
|||
|
|||
if (type > 2 && type <= 4) |
|||
{ |
|||
d = this.ReadFix16(); |
|||
} |
|||
|
|||
if (type == 4) |
|||
{ |
|||
e = this.ReadFix16(); |
|||
f = this.ReadFix16(); |
|||
} |
|||
|
|||
switch (type) |
|||
{ |
|||
case 0: return new IccParametricCurve(gamma); |
|||
case 1: return new IccParametricCurve(gamma, a, b); |
|||
case 2: return new IccParametricCurve(gamma, a, b, c); |
|||
case 3: return new IccParametricCurve(gamma, a, b, c, d); |
|||
case 4: return new IccParametricCurve(gamma, a, b, c, d, e, f); |
|||
default: throw new InvalidIccProfileException($"Invalid parametric curve type of {type}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccCurveSegment"/>
|
|||
/// </summary>
|
|||
/// <returns>The read segment</returns>
|
|||
public IccCurveSegment ReadCurveSegment() |
|||
{ |
|||
IccCurveSegmentSignature signature = (IccCurveSegmentSignature)this.ReadUInt32(); |
|||
this.AddIndex(4); // 4 bytes reserved
|
|||
|
|||
switch (signature) |
|||
{ |
|||
case IccCurveSegmentSignature.FormulaCurve: |
|||
return this.ReadFormulaCurveElement(); |
|||
case IccCurveSegmentSignature.SampledCurve: |
|||
return this.ReadSampledCurveElement(); |
|||
default: |
|||
throw new InvalidIccProfileException($"Invalid curve segment type of {signature}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccFormulaCurveElement"/>
|
|||
/// </summary>
|
|||
/// <returns>The read segment</returns>
|
|||
public IccFormulaCurveElement ReadFormulaCurveElement() |
|||
{ |
|||
IccFormulaCurveType type = (IccFormulaCurveType)this.ReadUInt16(); |
|||
this.AddIndex(2); // 2 bytes reserved
|
|||
double gamma, a, b, c, d, e; |
|||
gamma = a = b = c = d = e = 0; |
|||
|
|||
if (type == IccFormulaCurveType.Type1 || type == IccFormulaCurveType.Type2) |
|||
{ |
|||
gamma = this.ReadSingle(); |
|||
} |
|||
|
|||
a = this.ReadSingle(); |
|||
b = this.ReadSingle(); |
|||
c = this.ReadSingle(); |
|||
|
|||
if (type == IccFormulaCurveType.Type2 || type == IccFormulaCurveType.Type3) |
|||
{ |
|||
d = this.ReadSingle(); |
|||
} |
|||
|
|||
if (type == IccFormulaCurveType.Type3) |
|||
{ |
|||
e = this.ReadSingle(); |
|||
} |
|||
|
|||
return new IccFormulaCurveElement(type, gamma, a, b, c, d, e); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccSampledCurveElement"/>
|
|||
/// </summary>
|
|||
/// <returns>The read segment</returns>
|
|||
public IccSampledCurveElement ReadSampledCurveElement() |
|||
{ |
|||
uint count = this.ReadUInt32(); |
|||
float[] entries = new float[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
entries[i] = this.ReadSingle(); |
|||
} |
|||
|
|||
return new IccSampledCurveElement(entries); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads curve data
|
|||
/// </summary>
|
|||
/// <param name="count">Number of input channels</param>
|
|||
/// <returns>The curve data</returns>
|
|||
private IccTagDataEntry[] ReadCurves(int count) |
|||
{ |
|||
IccTagDataEntry[] tdata = new IccTagDataEntry[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
|||
if (type != IccTypeSignature.Curve && type != IccTypeSignature.ParametricCurve) |
|||
{ |
|||
throw new InvalidIccProfileException($"Curve has to be either \"{nameof(IccTypeSignature)}.{nameof(IccTypeSignature.Curve)}\" or" + |
|||
$" \"{nameof(IccTypeSignature)}.{nameof(IccTypeSignature.ParametricCurve)}\" for LutAToB- and LutBToA-TagDataEntries"); |
|||
} |
|||
|
|||
if (type == IccTypeSignature.Curve) |
|||
{ |
|||
tdata[i] = this.ReadCurveTagDataEntry(); |
|||
} |
|||
else if (type == IccTypeSignature.ParametricCurve) |
|||
{ |
|||
tdata[i] = this.ReadParametricCurveTagDataEntry(); |
|||
} |
|||
|
|||
this.AddPadding(); |
|||
} |
|||
|
|||
return tdata; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
// <copyright file="IccDataReader.Lut.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads an 8bit lookup table
|
|||
/// </summary>
|
|||
/// <returns>The read LUT</returns>
|
|||
public IccLut ReadLUT8() |
|||
{ |
|||
return new IccLut(this.ReadBytes(256)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a 16bit lookup table
|
|||
/// </summary>
|
|||
/// <param name="count">The number of entries</param>
|
|||
/// <returns>The read LUT</returns>
|
|||
public IccLut ReadLUT16(int count) |
|||
{ |
|||
ushort[] values = new ushort[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
values[i] = this.ReadUInt16(); |
|||
} |
|||
|
|||
return new IccLut(values); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a CLUT depending on type
|
|||
/// </summary>
|
|||
/// <param name="inChannelCount">Input channel count</param>
|
|||
/// <param name="outChannelCount">Output channel count</param>
|
|||
/// <param name="isFloat">If true, it's read as CLUTf32,
|
|||
/// else read as either CLUT8 or CLUT16 depending on embedded information</param>
|
|||
/// <returns>The read CLUT</returns>
|
|||
public IccClut ReadCLUT(int inChannelCount, int outChannelCount, bool isFloat) |
|||
{ |
|||
// Grid-points are always 16 bytes long but only 0-inChCount are used
|
|||
byte[] gridPointCount = new byte[inChannelCount]; |
|||
Buffer.BlockCopy(this.data, this.AddIndex(16), gridPointCount, 0, inChannelCount); |
|||
|
|||
if (!isFloat) |
|||
{ |
|||
byte size = this.data[this.AddIndex(4)]; // First byte is info, last 3 bytes are reserved
|
|||
if (size == 1) |
|||
{ |
|||
return this.ReadCLUT8(inChannelCount, outChannelCount, gridPointCount); |
|||
} |
|||
else if (size == 2) |
|||
{ |
|||
return this.ReadCLUT16(inChannelCount, outChannelCount, gridPointCount); |
|||
} |
|||
else |
|||
{ |
|||
throw new InvalidIccProfileException($"Invalid CLUT size of {size}"); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return this.ReadCLUTf32(inChannelCount, outChannelCount, gridPointCount); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an 8 bit CLUT
|
|||
/// </summary>
|
|||
/// <param name="inChannelCount">Input channel count</param>
|
|||
/// <param name="outChannelCount">Output channel count</param>
|
|||
/// <param name="gridPointCount">Grid point count for each CLUT channel</param>
|
|||
/// <returns>The read CLUT8</returns>
|
|||
public IccClut ReadCLUT8(int inChannelCount, int outChannelCount, byte[] gridPointCount) |
|||
{ |
|||
int start = this.index; |
|||
int length = 0; |
|||
for (int i = 0; i < inChannelCount; i++) |
|||
{ |
|||
length += (int)Math.Pow(gridPointCount[i], inChannelCount); |
|||
} |
|||
|
|||
length /= inChannelCount; |
|||
|
|||
const float max = byte.MaxValue; |
|||
|
|||
float[][] values = new float[length][]; |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
values[i] = new float[outChannelCount]; |
|||
for (int j = 0; j < outChannelCount; j++) |
|||
{ |
|||
values[i][j] = this.data[this.index++] / max; |
|||
} |
|||
} |
|||
|
|||
this.index = start + (length * outChannelCount); |
|||
return new IccClut(values, gridPointCount, IccClutDataType.UInt8); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a 16 bit CLUT
|
|||
/// </summary>
|
|||
/// <param name="inChannelCount">Input channel count</param>
|
|||
/// <param name="outChannelCount">Output channel count</param>
|
|||
/// <param name="gridPointCount">Grid point count for each CLUT channel</param>
|
|||
/// <returns>The read CLUT16</returns>
|
|||
public IccClut ReadCLUT16(int inChannelCount, int outChannelCount, byte[] gridPointCount) |
|||
{ |
|||
int start = this.index; |
|||
int length = 0; |
|||
for (int i = 0; i < inChannelCount; i++) |
|||
{ |
|||
length += (int)Math.Pow(gridPointCount[i], inChannelCount); |
|||
} |
|||
|
|||
length /= inChannelCount; |
|||
|
|||
const float max = ushort.MaxValue; |
|||
|
|||
float[][] values = new float[length][]; |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
values[i] = new float[outChannelCount]; |
|||
for (int j = 0; j < outChannelCount; j++) |
|||
{ |
|||
values[i][j] = this.ReadUInt16() / max; |
|||
} |
|||
} |
|||
|
|||
this.index = start + (length * outChannelCount * 2); |
|||
return new IccClut(values, gridPointCount, IccClutDataType.UInt16); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a 32bit floating point CLUT
|
|||
/// </summary>
|
|||
/// <param name="inChCount">Input channel count</param>
|
|||
/// <param name="outChCount">Output channel count</param>
|
|||
/// <param name="gridPointCount">Grid point count for each CLUT channel</param>
|
|||
/// <returns>The read CLUTf32</returns>
|
|||
public IccClut ReadCLUTf32(int inChCount, int outChCount, byte[] gridPointCount) |
|||
{ |
|||
int start = this.index; |
|||
int length = 0; |
|||
for (int i = 0; i < inChCount; i++) |
|||
{ |
|||
length += (int)Math.Pow(gridPointCount[i], inChCount); |
|||
} |
|||
|
|||
length /= inChCount; |
|||
|
|||
float[][] values = new float[length][]; |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
values[i] = new float[outChCount]; |
|||
for (int j = 0; j < outChCount; j++) |
|||
{ |
|||
values[i][j] = this.ReadSingle(); |
|||
} |
|||
} |
|||
|
|||
this.index = start + (length * outChCount * 4); |
|||
return new IccClut(values, gridPointCount, IccClutDataType.Float); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// <copyright file="IccDataReader.Matrix.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads a two dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="xCount">Number of values in X</param>
|
|||
/// <param name="yCount">Number of values in Y</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The read matrix</returns>
|
|||
public float[,] ReadMatrix(int xCount, int yCount, bool isSingle) |
|||
{ |
|||
float[,] matrix = new float[xCount, yCount]; |
|||
for (int y = 0; y < yCount; y++) |
|||
{ |
|||
for (int x = 0; x < xCount; x++) |
|||
{ |
|||
if (isSingle) |
|||
{ |
|||
matrix[x, y] = this.ReadSingle(); |
|||
} |
|||
else |
|||
{ |
|||
matrix[x, y] = this.ReadFix16(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return matrix; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a one dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="yCount">Number of values</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The read matrix</returns>
|
|||
public float[] ReadMatrix(int yCount, bool isSingle) |
|||
{ |
|||
float[] matrix = new float[yCount]; |
|||
for (int i = 0; i < yCount; i++) |
|||
{ |
|||
if (isSingle) |
|||
{ |
|||
matrix[i] = this.ReadSingle(); |
|||
} |
|||
else |
|||
{ |
|||
matrix[i] = this.ReadFix16(); |
|||
} |
|||
} |
|||
|
|||
return matrix; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// <copyright file="IccDataReader.MultiProcessElement.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads a <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <returns>The read <see cref="IccMultiProcessElement"/></returns>
|
|||
public IccMultiProcessElement ReadMultiProcessElement() |
|||
{ |
|||
IccMultiProcessElementSignature signature = (IccMultiProcessElementSignature)this.ReadUInt32(); |
|||
ushort inChannelCount = this.ReadUInt16(); |
|||
ushort outChannelCount = this.ReadUInt16(); |
|||
|
|||
switch (signature) |
|||
{ |
|||
case IccMultiProcessElementSignature.CurveSet: |
|||
return this.ReadCurveSetProcessElement(inChannelCount, outChannelCount); |
|||
case IccMultiProcessElementSignature.Matrix: |
|||
return this.ReadMatrixProcessElement(inChannelCount, outChannelCount); |
|||
case IccMultiProcessElementSignature.Clut: |
|||
return this.ReadCLUTProcessElement(inChannelCount, outChannelCount); |
|||
|
|||
// Currently just placeholders for future ICC expansion
|
|||
case IccMultiProcessElementSignature.BAcs: |
|||
this.AddIndex(8); |
|||
return new IccBAcsProcessElement(inChannelCount, outChannelCount); |
|||
case IccMultiProcessElementSignature.EAcs: |
|||
this.AddIndex(8); |
|||
return new IccEAcsProcessElement(inChannelCount, outChannelCount); |
|||
|
|||
default: |
|||
throw new InvalidIccProfileException($"Invalid MultiProcessElement type of {signature}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a CurveSet <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="inChannelCount">Number of input channels</param>
|
|||
/// <param name="outChannelCount">Number of output channels</param>
|
|||
/// <returns>The read <see cref="IccCurveSetProcessElement"/></returns>
|
|||
public IccCurveSetProcessElement ReadCurveSetProcessElement(int inChannelCount, int outChannelCount) |
|||
{ |
|||
IccOneDimensionalCurve[] curves = new IccOneDimensionalCurve[inChannelCount]; |
|||
for (int i = 0; i < inChannelCount; i++) |
|||
{ |
|||
curves[i] = this.ReadOneDimensionalCurve(); |
|||
this.AddPadding(); |
|||
} |
|||
|
|||
return new IccCurveSetProcessElement(curves); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a Matrix <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="inChannelCount">Number of input channels</param>
|
|||
/// <param name="outChannelCount">Number of output channels</param>
|
|||
/// <returns>The read <see cref="IccMatrixProcessElement"/></returns>
|
|||
public IccMatrixProcessElement ReadMatrixProcessElement(int inChannelCount, int outChannelCount) |
|||
{ |
|||
return new IccMatrixProcessElement( |
|||
this.ReadMatrix(inChannelCount, outChannelCount, true), |
|||
this.ReadMatrix(outChannelCount, true)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a CLUT <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="inChCount">Number of input channels</param>
|
|||
/// <param name="outChCount">Number of output channels</param>
|
|||
/// <returns>The read <see cref="IccClutProcessElement"/></returns>
|
|||
public IccClutProcessElement ReadCLUTProcessElement(int inChCount, int outChCount) |
|||
{ |
|||
return new IccClutProcessElement(this.ReadCLUT(inChCount, outChCount, true)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,157 @@ |
|||
// <copyright file="IccDataReader.NonPrimitives.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads a DateTime
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public DateTime ReadDateTime() |
|||
{ |
|||
try |
|||
{ |
|||
return new DateTime( |
|||
year: this.ReadUInt16(), |
|||
month: this.ReadUInt16(), |
|||
day: this.ReadUInt16(), |
|||
hour: this.ReadUInt16(), |
|||
minute: this.ReadUInt16(), |
|||
second: this.ReadUInt16(), |
|||
kind: DateTimeKind.Utc); |
|||
} |
|||
catch (ArgumentOutOfRangeException) |
|||
{ |
|||
return DateTime.MinValue; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an ICC profile version number
|
|||
/// </summary>
|
|||
/// <returns>the version number</returns>
|
|||
public Version ReadVersionNumber() |
|||
{ |
|||
int version = this.ReadInt32(); |
|||
|
|||
int major = (version >> 24) & 0xFF; |
|||
int minor = (version >> 20) & 0x0F; |
|||
int bugfix = (version >> 16) & 0x0F; |
|||
|
|||
return new Version(major, minor, bugfix); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an XYZ number
|
|||
/// </summary>
|
|||
/// <returns>the XYZ number</returns>
|
|||
public Vector3 ReadXyzNumber() |
|||
{ |
|||
return new Vector3( |
|||
x: this.ReadFix16(), |
|||
y: this.ReadFix16(), |
|||
z: this.ReadFix16()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a profile ID
|
|||
/// </summary>
|
|||
/// <returns>the profile ID</returns>
|
|||
public IccProfileId ReadProfileId() |
|||
{ |
|||
return new IccProfileId( |
|||
p1: this.ReadUInt32(), |
|||
p2: this.ReadUInt32(), |
|||
p3: this.ReadUInt32(), |
|||
p4: this.ReadUInt32()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a position number
|
|||
/// </summary>
|
|||
/// <returns>the position number</returns>
|
|||
public IccPositionNumber ReadPositionNumber() |
|||
{ |
|||
return new IccPositionNumber( |
|||
offset: this.ReadUInt32(), |
|||
size: this.ReadUInt32()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a response number
|
|||
/// </summary>
|
|||
/// <returns>the response number</returns>
|
|||
public IccResponseNumber ReadResponseNumber() |
|||
{ |
|||
return new IccResponseNumber( |
|||
deviceCode: this.ReadUInt16(), |
|||
measurementValue: this.ReadFix16()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a named color
|
|||
/// </summary>
|
|||
/// <param name="deviceCoordCount">Number of device coordinates</param>
|
|||
/// <returns>the named color</returns>
|
|||
public IccNamedColor ReadNamedColor(uint deviceCoordCount) |
|||
{ |
|||
string name = this.ReadAsciiString(32); |
|||
ushort[] pcsCoord = new ushort[3] { this.ReadUInt16(), this.ReadUInt16(), this.ReadUInt16() }; |
|||
ushort[] deviceCoord = new ushort[deviceCoordCount]; |
|||
|
|||
for (int i = 0; i < deviceCoordCount; i++) |
|||
{ |
|||
deviceCoord[i] = this.ReadUInt16(); |
|||
} |
|||
|
|||
return new IccNamedColor(name, pcsCoord, deviceCoord); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a profile description
|
|||
/// </summary>
|
|||
/// <returns>the profile description</returns>
|
|||
public IccProfileDescription ReadProfileDescription() |
|||
{ |
|||
uint manufacturer = this.ReadUInt32(); |
|||
uint model = this.ReadUInt32(); |
|||
IccDeviceAttribute attributes = (IccDeviceAttribute)this.ReadInt64(); |
|||
IccProfileTag technologyInfo = (IccProfileTag)this.ReadUInt32(); |
|||
this.ReadCheckTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode); |
|||
IccMultiLocalizedUnicodeTagDataEntry manufacturerInfo = this.ReadMultiLocalizedUnicodeTagDataEntry(); |
|||
this.ReadCheckTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode); |
|||
IccMultiLocalizedUnicodeTagDataEntry modelInfo = this.ReadMultiLocalizedUnicodeTagDataEntry(); |
|||
|
|||
return new IccProfileDescription( |
|||
manufacturer, |
|||
model, |
|||
attributes, |
|||
technologyInfo, |
|||
manufacturerInfo.Texts, |
|||
modelInfo.Texts); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a colorant table entry
|
|||
/// </summary>
|
|||
/// <returns>the profile description</returns>
|
|||
public IccColorantTableEntry ReadColorantTableEntry() |
|||
{ |
|||
return new IccColorantTableEntry( |
|||
name: this.ReadAsciiString(32), |
|||
pcs1: this.ReadUInt16(), |
|||
pcs2: this.ReadUInt16(), |
|||
pcs3: this.ReadUInt16()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,178 @@ |
|||
// <copyright file="IccDataReader.Primitives.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Text; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads an ushort
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public ushort ReadUInt16() |
|||
{ |
|||
return this.converter.ToUInt16(this.data, this.AddIndex(2)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a short
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public short ReadInt16() |
|||
{ |
|||
return this.converter.ToInt16(this.data, this.AddIndex(2)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an uint
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public uint ReadUInt32() |
|||
{ |
|||
return this.converter.ToUInt32(this.data, this.AddIndex(4)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an int
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public int ReadInt32() |
|||
{ |
|||
return this.converter.ToInt32(this.data, this.AddIndex(4)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an ulong
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public ulong ReadUInt64() |
|||
{ |
|||
return this.converter.ToUInt64(this.data, this.AddIndex(8)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a long
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public long ReadInt64() |
|||
{ |
|||
return this.converter.ToInt64(this.data, this.AddIndex(8)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a float
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public float ReadSingle() |
|||
{ |
|||
return this.converter.ToSingle(this.data, this.AddIndex(4)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a double
|
|||
/// </summary>
|
|||
/// <returns>the value</returns>
|
|||
public double ReadDouble() |
|||
{ |
|||
return this.converter.ToDouble(this.data, this.AddIndex(8)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an ASCII encoded string
|
|||
/// </summary>
|
|||
/// <param name="length">number of bytes to read</param>
|
|||
/// <returns>The value as a string</returns>
|
|||
public string ReadAsciiString(int length) |
|||
{ |
|||
if (length == 0) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
Guard.MustBeGreaterThan(length, 0, nameof(length)); |
|||
string value = AsciiEncoding.GetString(this.data, this.AddIndex(length), length); |
|||
|
|||
// remove data after (potential) null terminator
|
|||
int pos = value.IndexOf('\0'); |
|||
if (pos >= 0) |
|||
{ |
|||
value = value.Substring(0, pos); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an UTF-16 big-endian encoded string
|
|||
/// </summary>
|
|||
/// <param name="length">number of bytes to read</param>
|
|||
/// <returns>The value as a string</returns>
|
|||
public string ReadUnicodeString(int length) |
|||
{ |
|||
if (length == 0) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
Guard.MustBeGreaterThan(length, 0, nameof(length)); |
|||
|
|||
return Encoding.BigEndianUnicode.GetString(this.data, this.AddIndex(length), length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a signed 32bit number with 1 sign bit, 15 value bits and 16 fractional bits
|
|||
/// </summary>
|
|||
/// <returns>The number as double</returns>
|
|||
public float ReadFix16() |
|||
{ |
|||
return this.ReadInt32() / 65536f; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an unsigned 32bit number with 16 value bits and 16 fractional bits
|
|||
/// </summary>
|
|||
/// <returns>The number as double</returns>
|
|||
public float ReadUFix16() |
|||
{ |
|||
return this.ReadUInt32() / 65536f; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an unsigned 16bit number with 1 value bit and 15 fractional bits
|
|||
/// </summary>
|
|||
/// <returns>The number as double</returns>
|
|||
public float ReadU1Fix15() |
|||
{ |
|||
return this.ReadUInt16() / 32768f; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads an unsigned 16bit number with 8 value bits and 8 fractional bits
|
|||
/// </summary>
|
|||
/// <returns>The number as double</returns>
|
|||
public float ReadUFix8() |
|||
{ |
|||
return this.ReadUInt16() / 256f; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a number of bytes and advances the index
|
|||
/// </summary>
|
|||
/// <param name="count">The number of bytes to read</param>
|
|||
/// <returns>The read bytes</returns>
|
|||
public byte[] ReadBytes(int count) |
|||
{ |
|||
byte[] bytes = new byte[count]; |
|||
Buffer.BlockCopy(this.data, this.AddIndex(count), bytes, 0, count); |
|||
return bytes; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,795 @@ |
|||
// <copyright file="IccDataReader.TagDataEntry.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
/// <summary>
|
|||
/// Reads a tag data entry
|
|||
/// </summary>
|
|||
/// <param name="info">The table entry with reading information</param>
|
|||
/// <returns>the tag data entry</returns>
|
|||
public IccTagDataEntry ReadTagDataEntry(IccTagTableEntry info) |
|||
{ |
|||
this.index = (int)info.Offset; |
|||
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
|||
|
|||
switch (type) |
|||
{ |
|||
case IccTypeSignature.Chromaticity: |
|||
return this.ReadChromaticityTagDataEntry(); |
|||
case IccTypeSignature.ColorantOrder: |
|||
return this.ReadColorantOrderTagDataEntry(); |
|||
case IccTypeSignature.ColorantTable: |
|||
return this.ReadColorantTableTagDataEntry(); |
|||
case IccTypeSignature.Curve: |
|||
return this.ReadCurveTagDataEntry(); |
|||
case IccTypeSignature.Data: |
|||
return this.ReadDataTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.DateTime: |
|||
return this.ReadDateTimeTagDataEntry(); |
|||
case IccTypeSignature.Lut16: |
|||
return this.ReadLut16TagDataEntry(); |
|||
case IccTypeSignature.Lut8: |
|||
return this.ReadLut8TagDataEntry(); |
|||
case IccTypeSignature.LutAToB: |
|||
return this.ReadLutAToBTagDataEntry(); |
|||
case IccTypeSignature.LutBToA: |
|||
return this.ReadLutBToATagDataEntry(); |
|||
case IccTypeSignature.Measurement: |
|||
return this.ReadMeasurementTagDataEntry(); |
|||
case IccTypeSignature.MultiLocalizedUnicode: |
|||
return this.ReadMultiLocalizedUnicodeTagDataEntry(); |
|||
case IccTypeSignature.MultiProcessElements: |
|||
return this.ReadMultiProcessElementsTagDataEntry(); |
|||
case IccTypeSignature.NamedColor2: |
|||
return this.ReadNamedColor2TagDataEntry(); |
|||
case IccTypeSignature.ParametricCurve: |
|||
return this.ReadParametricCurveTagDataEntry(); |
|||
case IccTypeSignature.ProfileSequenceDesc: |
|||
return this.ReadProfileSequenceDescTagDataEntry(); |
|||
case IccTypeSignature.ProfileSequenceIdentifier: |
|||
return this.ReadProfileSequenceIdentifierTagDataEntry(); |
|||
case IccTypeSignature.ResponseCurveSet16: |
|||
return this.ReadResponseCurveSet16TagDataEntry(); |
|||
case IccTypeSignature.S15Fixed16Array: |
|||
return this.ReadFix16ArrayTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.Signature: |
|||
return this.ReadSignatureTagDataEntry(); |
|||
case IccTypeSignature.Text: |
|||
return this.ReadTextTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.U16Fixed16Array: |
|||
return this.ReadUFix16ArrayTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.UInt16Array: |
|||
return this.ReadUInt16ArrayTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.UInt32Array: |
|||
return this.ReadUInt32ArrayTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.UInt64Array: |
|||
return this.ReadUInt64ArrayTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.UInt8Array: |
|||
return this.ReadUInt8ArrayTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.ViewingConditions: |
|||
return this.ReadViewingConditionsTagDataEntry(info.DataSize); |
|||
case IccTypeSignature.Xyz: |
|||
return this.ReadXyzTagDataEntry(info.DataSize); |
|||
|
|||
// V2 Type:
|
|||
case IccTypeSignature.TextDescription: |
|||
return this.ReadTextDescriptionTagDataEntry(); |
|||
|
|||
case IccTypeSignature.Unknown: |
|||
default: |
|||
return this.ReadUnknownTagDataEntry(info.DataSize); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads the header of a <see cref="IccTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read signature</returns>
|
|||
public IccTypeSignature ReadTagDataEntryHeader() |
|||
{ |
|||
IccTypeSignature type = (IccTypeSignature)this.ReadUInt32(); |
|||
this.AddIndex(4); // 4 bytes are not used
|
|||
return type; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads the header of a <see cref="IccTagDataEntry"/> and checks if it's the expected value
|
|||
/// </summary>
|
|||
/// <param name="expected">expected value to check against</param>
|
|||
public void ReadCheckTagDataEntryHeader(IccTypeSignature expected) |
|||
{ |
|||
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
|||
if (expected != (IccTypeSignature)uint.MaxValue && type != expected) |
|||
{ |
|||
throw new InvalidIccProfileException($"Read signature {type} is not the expected {expected}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccTagDataEntry"/> with an unknown <see cref="IccTypeSignature"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size) |
|||
{ |
|||
int count = (int)size - 8; // 8 is the tag header size
|
|||
return new IccUnknownTagDataEntry(this.ReadBytes(count)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccChromaticityTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() |
|||
{ |
|||
ushort channelCount = this.ReadUInt16(); |
|||
IccColorantEncoding colorant = (IccColorantEncoding)this.ReadUInt16(); |
|||
|
|||
if (Enum.IsDefined(typeof(IccColorantEncoding), colorant) && colorant != IccColorantEncoding.Unknown) |
|||
{ |
|||
// The type is known and so are the values (they are constant)
|
|||
// channelCount should always be 3 but it doesn't really matter if it's not
|
|||
return new IccChromaticityTagDataEntry(colorant); |
|||
} |
|||
else |
|||
{ |
|||
// The type is not know, so the values need be read
|
|||
double[][] values = new double[channelCount][]; |
|||
for (int i = 0; i < channelCount; i++) |
|||
{ |
|||
values[i] = new double[2]; |
|||
values[i][0] = this.ReadUFix16(); |
|||
values[i][1] = this.ReadUFix16(); |
|||
} |
|||
|
|||
return new IccChromaticityTagDataEntry(values); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccColorantOrderTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry() |
|||
{ |
|||
uint colorantCount = this.ReadUInt32(); |
|||
byte[] number = this.ReadBytes((int)colorantCount); |
|||
return new IccColorantOrderTagDataEntry(number); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccColorantTableTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry() |
|||
{ |
|||
uint colorantCount = this.ReadUInt32(); |
|||
IccColorantTableEntry[] cdata = new IccColorantTableEntry[colorantCount]; |
|||
for (int i = 0; i < colorantCount; i++) |
|||
{ |
|||
cdata[i] = this.ReadColorantTableEntry(); |
|||
} |
|||
|
|||
return new IccColorantTableTagDataEntry(cdata); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccCurveTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccCurveTagDataEntry ReadCurveTagDataEntry() |
|||
{ |
|||
uint pointCount = this.ReadUInt32(); |
|||
|
|||
if (pointCount == 0) |
|||
{ |
|||
return new IccCurveTagDataEntry(); |
|||
} |
|||
else if (pointCount == 1) |
|||
{ |
|||
return new IccCurveTagDataEntry(this.ReadUFix8()); |
|||
} |
|||
else |
|||
{ |
|||
float[] cdata = new float[pointCount]; |
|||
for (int i = 0; i < pointCount; i++) |
|||
{ |
|||
cdata[i] = this.ReadUInt16() / 65535f; |
|||
} |
|||
|
|||
return new IccCurveTagDataEntry(cdata); |
|||
} |
|||
|
|||
// TODO: If the input is PCSXYZ, 1+(32 767/32 768) shall be mapped to the value 1,0. If the output is PCSXYZ, the value 1,0 shall be mapped to 1+(32 767/32 768).
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccDataTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccDataTagDataEntry ReadDataTagDataEntry(uint size) |
|||
{ |
|||
this.AddIndex(3); // first 3 bytes are zero
|
|||
byte b = this.data[this.AddIndex(1)]; |
|||
|
|||
// last bit of 4th byte is either 0 = ASCII or 1 = binary
|
|||
bool ascii = this.GetBit(b, 7); |
|||
int length = (int)size - 12; |
|||
byte[] cdata = this.ReadBytes(length); |
|||
|
|||
return new IccDataTagDataEntry(cdata, ascii); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccDateTimeTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() |
|||
{ |
|||
return new IccDateTimeTagDataEntry(this.ReadDateTime()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccLut16TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccLut16TagDataEntry ReadLut16TagDataEntry() |
|||
{ |
|||
byte inChCount = this.data[this.AddIndex(1)]; |
|||
byte outChCount = this.data[this.AddIndex(1)]; |
|||
byte clutPointCount = this.data[this.AddIndex(1)]; |
|||
this.AddIndex(1); // 1 byte reserved
|
|||
|
|||
float[,] matrix = this.ReadMatrix(3, 3, false); |
|||
|
|||
ushort inTableCount = this.ReadUInt16(); |
|||
ushort outTableCount = this.ReadUInt16(); |
|||
|
|||
// Input LUT
|
|||
IccLut[] inValues = new IccLut[inChCount]; |
|||
byte[] gridPointCount = new byte[inChCount]; |
|||
for (int i = 0; i < inChCount; i++) |
|||
{ |
|||
inValues[i] = this.ReadLUT16(inTableCount); |
|||
gridPointCount[i] = clutPointCount; |
|||
} |
|||
|
|||
// CLUT
|
|||
IccClut clut = this.ReadCLUT16(inChCount, outChCount, gridPointCount); |
|||
|
|||
// Output LUT
|
|||
IccLut[] outValues = new IccLut[outChCount]; |
|||
for (int i = 0; i < outChCount; i++) |
|||
{ |
|||
outValues[i] = this.ReadLUT16(outTableCount); |
|||
} |
|||
|
|||
return new IccLut16TagDataEntry(matrix, inValues, clut, outValues); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccLut8TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccLut8TagDataEntry ReadLut8TagDataEntry() |
|||
{ |
|||
byte inChCount = this.data[this.AddIndex(1)]; |
|||
byte outChCount = this.data[this.AddIndex(1)]; |
|||
byte clutPointCount = this.data[this.AddIndex(1)]; |
|||
this.AddIndex(1); // 1 byte reserved
|
|||
|
|||
float[,] matrix = this.ReadMatrix(3, 3, false); |
|||
|
|||
// Input LUT
|
|||
IccLut[] inValues = new IccLut[inChCount]; |
|||
byte[] gridPointCount = new byte[inChCount]; |
|||
for (int i = 0; i < inChCount; i++) |
|||
{ |
|||
inValues[i] = this.ReadLUT8(); |
|||
gridPointCount[i] = clutPointCount; |
|||
} |
|||
|
|||
// CLUT
|
|||
IccClut clut = this.ReadCLUT8(inChCount, outChCount, gridPointCount); |
|||
|
|||
// Output LUT
|
|||
IccLut[] outValues = new IccLut[outChCount]; |
|||
for (int i = 0; i < outChCount; i++) |
|||
{ |
|||
outValues[i] = this.ReadLUT8(); |
|||
} |
|||
|
|||
return new IccLut8TagDataEntry(matrix, inValues, clut, outValues); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccLutAToBTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccLutAToBTagDataEntry ReadLutAToBTagDataEntry() |
|||
{ |
|||
int start = this.index - 8; // 8 is the tag header size
|
|||
|
|||
byte inChCount = this.data[this.AddIndex(1)]; |
|||
byte outChCount = this.data[this.AddIndex(1)]; |
|||
this.AddIndex(2); // 2 bytes reserved
|
|||
|
|||
uint bCurveOffset = this.ReadUInt32(); |
|||
uint matrixOffset = this.ReadUInt32(); |
|||
uint mCurveOffset = this.ReadUInt32(); |
|||
uint clutOffset = this.ReadUInt32(); |
|||
uint aCurveOffset = this.ReadUInt32(); |
|||
|
|||
IccTagDataEntry[] bCurve = null; |
|||
IccTagDataEntry[] mCurve = null; |
|||
IccTagDataEntry[] aCurve = null; |
|||
IccClut clut = null; |
|||
float[,] matrix3x3 = null; |
|||
float[] matrix3x1 = null; |
|||
|
|||
if (bCurveOffset != 0) |
|||
{ |
|||
this.index = (int)bCurveOffset + start; |
|||
bCurve = this.ReadCurves(outChCount); |
|||
} |
|||
|
|||
if (mCurveOffset != 0) |
|||
{ |
|||
this.index = (int)mCurveOffset + start; |
|||
mCurve = this.ReadCurves(outChCount); |
|||
} |
|||
|
|||
if (aCurveOffset != 0) |
|||
{ |
|||
this.index = (int)aCurveOffset + start; |
|||
aCurve = this.ReadCurves(inChCount); |
|||
} |
|||
|
|||
if (clutOffset != 0) |
|||
{ |
|||
this.index = (int)clutOffset + start; |
|||
clut = this.ReadCLUT(inChCount, outChCount, false); |
|||
} |
|||
|
|||
if (matrixOffset != 0) |
|||
{ |
|||
this.index = (int)matrixOffset + start; |
|||
matrix3x3 = this.ReadMatrix(3, 3, false); |
|||
matrix3x1 = this.ReadMatrix(3, false); |
|||
} |
|||
|
|||
return new IccLutAToBTagDataEntry(bCurve, matrix3x3, matrix3x1, mCurve, clut, aCurve); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccLutBToATagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccLutBToATagDataEntry ReadLutBToATagDataEntry() |
|||
{ |
|||
int start = this.index - 8; // 8 is the tag header size
|
|||
|
|||
byte inChCount = this.data[this.AddIndex(1)]; |
|||
byte outChCount = this.data[this.AddIndex(1)]; |
|||
this.AddIndex(2); // 2 bytes reserved
|
|||
|
|||
uint bCurveOffset = this.ReadUInt32(); |
|||
uint matrixOffset = this.ReadUInt32(); |
|||
uint mCurveOffset = this.ReadUInt32(); |
|||
uint clutOffset = this.ReadUInt32(); |
|||
uint aCurveOffset = this.ReadUInt32(); |
|||
|
|||
IccTagDataEntry[] bCurve = null; |
|||
IccTagDataEntry[] mCurve = null; |
|||
IccTagDataEntry[] aCurve = null; |
|||
IccClut clut = null; |
|||
float[,] matrix3x3 = null; |
|||
float[] matrix3x1 = null; |
|||
|
|||
if (bCurveOffset != 0) |
|||
{ |
|||
this.index = (int)bCurveOffset + start; |
|||
bCurve = this.ReadCurves(inChCount); |
|||
} |
|||
|
|||
if (mCurveOffset != 0) |
|||
{ |
|||
this.index = (int)mCurveOffset + start; |
|||
mCurve = this.ReadCurves(inChCount); |
|||
} |
|||
|
|||
if (aCurveOffset != 0) |
|||
{ |
|||
this.index = (int)aCurveOffset + start; |
|||
aCurve = this.ReadCurves(outChCount); |
|||
} |
|||
|
|||
if (clutOffset != 0) |
|||
{ |
|||
this.index = (int)clutOffset + start; |
|||
clut = this.ReadCLUT(inChCount, outChCount, false); |
|||
} |
|||
|
|||
if (matrixOffset != 0) |
|||
{ |
|||
this.index = (int)matrixOffset + start; |
|||
matrix3x3 = this.ReadMatrix(3, 3, false); |
|||
matrix3x1 = this.ReadMatrix(3, false); |
|||
} |
|||
|
|||
return new IccLutBToATagDataEntry(bCurve, matrix3x3, matrix3x1, mCurve, clut, aCurve); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccMeasurementTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() |
|||
{ |
|||
return new IccMeasurementTagDataEntry( |
|||
observer: (IccStandardObserver)this.ReadUInt32(), |
|||
xyzBacking: this.ReadXyzNumber(), |
|||
geometry: (IccMeasurementGeometry)this.ReadUInt32(), |
|||
flare: this.ReadUFix16(), |
|||
illuminant: (IccStandardIlluminant)this.ReadUInt32()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccMultiLocalizedUnicodeTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntry() |
|||
{ |
|||
int start = this.index - 8; // 8 is the tag header size
|
|||
uint recordCount = this.ReadUInt32(); |
|||
uint recordSize = this.ReadUInt32(); |
|||
IccLocalizedString[] text = new IccLocalizedString[recordCount]; |
|||
|
|||
string[] culture = new string[recordCount]; |
|||
uint[] length = new uint[recordCount]; |
|||
uint[] offset = new uint[recordCount]; |
|||
|
|||
for (int i = 0; i < recordCount; i++) |
|||
{ |
|||
culture[i] = $"{this.ReadAsciiString(2)}-{this.ReadAsciiString(2)}"; |
|||
length[i] = this.ReadUInt32(); |
|||
offset[i] = this.ReadUInt32(); |
|||
} |
|||
|
|||
for (int i = 0; i < recordCount; i++) |
|||
{ |
|||
this.index = (int)(start + offset[i]); |
|||
text[i] = new IccLocalizedString(new CultureInfo(culture[i]), this.ReadUnicodeString((int)length[i])); |
|||
} |
|||
|
|||
return new IccMultiLocalizedUnicodeTagDataEntry(text); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccMultiProcessElementsTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry() |
|||
{ |
|||
int start = this.index - 8; |
|||
|
|||
ushort inChannelCount = this.ReadUInt16(); |
|||
ushort outChannelCount = this.ReadUInt16(); |
|||
uint elementCount = this.ReadUInt32(); |
|||
|
|||
IccPositionNumber[] positionTable = new IccPositionNumber[elementCount]; |
|||
for (int i = 0; i < elementCount; i++) |
|||
{ |
|||
positionTable[i] = this.ReadPositionNumber(); |
|||
} |
|||
|
|||
IccMultiProcessElement[] elements = new IccMultiProcessElement[elementCount]; |
|||
for (int i = 0; i < elementCount; i++) |
|||
{ |
|||
this.index = (int)positionTable[i].Offset + start; |
|||
elements[i] = this.ReadMultiProcessElement(); |
|||
} |
|||
|
|||
return new IccMultiProcessElementsTagDataEntry(elements); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccNamedColor2TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() |
|||
{ |
|||
int vendorFlag = this.ReadInt32(); |
|||
uint colorCount = this.ReadUInt32(); |
|||
uint coordCount = this.ReadUInt32(); |
|||
string prefix = this.ReadAsciiString(32); |
|||
string suffix = this.ReadAsciiString(32); |
|||
|
|||
IccNamedColor[] colors = new IccNamedColor[colorCount]; |
|||
for (int i = 0; i < colorCount; i++) |
|||
{ |
|||
colors[i] = this.ReadNamedColor(coordCount); |
|||
} |
|||
|
|||
return new IccNamedColor2TagDataEntry(vendorFlag, prefix, suffix, colors); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccParametricCurveTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() |
|||
{ |
|||
return new IccParametricCurveTagDataEntry(this.ReadParametricCurve()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccProfileSequenceDescTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry() |
|||
{ |
|||
uint count = this.ReadUInt32(); |
|||
IccProfileDescription[] description = new IccProfileDescription[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
description[i] = this.ReadProfileDescription(); |
|||
} |
|||
|
|||
return new IccProfileSequenceDescTagDataEntry(description); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccProfileSequenceIdentifierTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTagDataEntry() |
|||
{ |
|||
int start = this.index - 8; // 8 is the tag header size
|
|||
uint count = this.ReadUInt32(); |
|||
IccPositionNumber[] table = new IccPositionNumber[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
table[i] = this.ReadPositionNumber(); |
|||
} |
|||
|
|||
IccProfileSequenceIdentifier[] entries = new IccProfileSequenceIdentifier[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
this.index = (int)(start + table[i].Offset); |
|||
IccProfileId id = this.ReadProfileId(); |
|||
this.ReadCheckTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode); |
|||
IccMultiLocalizedUnicodeTagDataEntry description = this.ReadMultiLocalizedUnicodeTagDataEntry(); |
|||
entries[i] = new IccProfileSequenceIdentifier(id, description.Texts); |
|||
} |
|||
|
|||
return new IccProfileSequenceIdentifierTagDataEntry(entries); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccResponseCurveSet16TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() |
|||
{ |
|||
int start = this.index - 8; // 8 is the tag header size
|
|||
ushort channelCount = this.ReadUInt16(); |
|||
ushort measurmentCount = this.ReadUInt16(); |
|||
|
|||
uint[] offset = new uint[measurmentCount]; |
|||
for (int i = 0; i < measurmentCount; i++) |
|||
{ |
|||
offset[i] = this.ReadUInt32(); |
|||
} |
|||
|
|||
IccResponseCurve[] curves = new IccResponseCurve[measurmentCount]; |
|||
for (int i = 0; i < measurmentCount; i++) |
|||
{ |
|||
this.index = (int)(start + offset[i]); |
|||
curves[i] = this.ReadResponseCurve(channelCount); |
|||
} |
|||
|
|||
return new IccResponseCurveSet16TagDataEntry(curves); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccFix16ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size) |
|||
{ |
|||
uint count = (size - 8) / 4; |
|||
float[] arrayData = new float[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
arrayData[i] = this.ReadFix16() / 256f; |
|||
} |
|||
|
|||
return new IccFix16ArrayTagDataEntry(arrayData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccSignatureTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccSignatureTagDataEntry ReadSignatureTagDataEntry() |
|||
{ |
|||
return new IccSignatureTagDataEntry(this.ReadAsciiString(4)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccTextTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccTextTagDataEntry ReadTextTagDataEntry(uint size) |
|||
{ |
|||
return new IccTextTagDataEntry(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccUFix16ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size) |
|||
{ |
|||
uint count = (size - 8) / 4; |
|||
float[] arrayData = new float[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
arrayData[i] = this.ReadUFix16(); |
|||
} |
|||
|
|||
return new IccUFix16ArrayTagDataEntry(arrayData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccUInt16ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size) |
|||
{ |
|||
uint count = (size - 8) / 2; |
|||
ushort[] arrayData = new ushort[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
arrayData[i] = this.ReadUInt16(); |
|||
} |
|||
|
|||
return new IccUInt16ArrayTagDataEntry(arrayData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccUInt32ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size) |
|||
{ |
|||
uint count = (size - 8) / 4; |
|||
uint[] arrayData = new uint[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
arrayData[i] = this.ReadUInt32(); |
|||
} |
|||
|
|||
return new IccUInt32ArrayTagDataEntry(arrayData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccUInt64ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size) |
|||
{ |
|||
uint count = (size - 8) / 8; |
|||
ulong[] arrayData = new ulong[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
arrayData[i] = this.ReadUInt64(); |
|||
} |
|||
|
|||
return new IccUInt64ArrayTagDataEntry(arrayData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccUInt8ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size) |
|||
{ |
|||
int count = (int)size - 8; // 8 is the tag header size
|
|||
byte[] adata = this.ReadBytes(count); |
|||
|
|||
return new IccUInt8ArrayTagDataEntry(adata); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccViewingConditionsTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry(uint size) |
|||
{ |
|||
return new IccViewingConditionsTagDataEntry( |
|||
illuminantXyz: this.ReadXyzNumber(), |
|||
surroundXyz: this.ReadXyzNumber(), |
|||
illuminant: (IccStandardIlluminant)this.ReadUInt32()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccXyzTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the entry in bytes</param>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) |
|||
{ |
|||
uint count = (size - 8) / 12; |
|||
Vector3[] arrayData = new Vector3[count]; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
arrayData[i] = this.ReadXyzNumber(); |
|||
} |
|||
|
|||
return new IccXyzTagDataEntry(arrayData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a <see cref="IccTextDescriptionTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <returns>The read entry</returns>
|
|||
public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() |
|||
{ |
|||
string asciiValue, unicodeValue, scriptcodeValue; |
|||
asciiValue = unicodeValue = scriptcodeValue = null; |
|||
|
|||
int asciiCount = (int)this.ReadUInt32(); |
|||
if (asciiCount > 0) |
|||
{ |
|||
asciiValue = this.ReadAsciiString(asciiCount - 1); |
|||
this.AddIndex(1); // Null terminator
|
|||
} |
|||
|
|||
uint unicodeLangCode = this.ReadUInt32(); |
|||
int unicodeCount = (int)this.ReadUInt32(); |
|||
if (unicodeCount > 0) |
|||
{ |
|||
unicodeValue = this.ReadUnicodeString((unicodeCount * 2) - 2); |
|||
this.AddIndex(2); // Null terminator
|
|||
} |
|||
|
|||
ushort scriptcodeCode = this.ReadUInt16(); |
|||
int scriptcodeCount = Math.Min(this.data[this.AddIndex(1)], (byte)67); |
|||
if (scriptcodeCount > 0) |
|||
{ |
|||
scriptcodeValue = this.ReadAsciiString(scriptcodeCount - 1); |
|||
this.AddIndex(1); // Null terminator
|
|||
} |
|||
|
|||
return new IccTextDescriptionTagDataEntry( |
|||
asciiValue, |
|||
unicodeValue, |
|||
scriptcodeValue, |
|||
unicodeLangCode, |
|||
scriptcodeCode); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
// <copyright file="IccDataReader.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Text; |
|||
using ImageSharp.IO; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to read ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataReader |
|||
{ |
|||
private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian; |
|||
private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); |
|||
|
|||
/// <summary>
|
|||
/// The data that is read
|
|||
/// </summary>
|
|||
private readonly byte[] data; |
|||
|
|||
/// <summary>
|
|||
/// The current reading position
|
|||
/// </summary>
|
|||
private int index; |
|||
|
|||
private EndianBitConverter converter = new BigEndianBitConverter(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IccDataReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="data">The data to read</param>
|
|||
public IccDataReader(byte[] data) |
|||
{ |
|||
Guard.NotNull(data, nameof(data)); |
|||
this.data = data; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the reading position to the given value
|
|||
/// </summary>
|
|||
/// <param name="index">The new index position</param>
|
|||
public void SetIndex(int index) |
|||
{ |
|||
this.index = index.Clamp(0, this.data.Length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the current <see cref="index"/> without increment and adds the given increment
|
|||
/// </summary>
|
|||
/// <param name="increment">The value to increment <see cref="index"/></param>
|
|||
/// <returns>The current <see cref="index"/> without the increment</returns>
|
|||
private int AddIndex(int increment) |
|||
{ |
|||
int tmp = this.index; |
|||
this.index += increment; |
|||
return tmp; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the 4 byte padding and adds it to the <see cref="index"/> variable
|
|||
/// </summary>
|
|||
private void AddPadding() |
|||
{ |
|||
this.index += this.CalcPadding(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the 4 byte padding
|
|||
/// </summary>
|
|||
/// <returns>the number of bytes to pad</returns>
|
|||
private int CalcPadding() |
|||
{ |
|||
int p = 4 - (this.index % 4); |
|||
return p >= 4 ? 0 : p; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the bit value at a specified position
|
|||
/// </summary>
|
|||
/// <param name="value">The value from where the bit will be extracted</param>
|
|||
/// <param name="position">Position of the bit. Zero based index from left to right.</param>
|
|||
/// <returns>The bit value at specified position</returns>
|
|||
private bool GetBit(byte value, int position) |
|||
{ |
|||
return ((value >> (7 - position)) & 1) == 1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the bit value at a specified position
|
|||
/// </summary>
|
|||
/// <param name="value">The value from where the bit will be extracted</param>
|
|||
/// <param name="position">Position of the bit. Zero based index from left to right.</param>
|
|||
/// <returns>The bit value at specified position</returns>
|
|||
private bool GetBit(ushort value, int position) |
|||
{ |
|||
return ((value >> (15 - position)) & 1) == 1; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,179 @@ |
|||
// <copyright file="IccDataWriter.Curves.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes a <see cref="IccOneDimensionalCurve"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The curve to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteOneDimensionalCurve(IccOneDimensionalCurve value) |
|||
{ |
|||
int count = this.WriteUInt16((ushort)value.Segments.Length); |
|||
count += this.WriteEmpty(2); |
|||
|
|||
foreach (double point in value.BreakPoints) |
|||
{ |
|||
count += this.WriteSingle((float)point); |
|||
} |
|||
|
|||
foreach (IccCurveSegment segment in value.Segments) |
|||
{ |
|||
count += this.WriteCurveSegment(segment); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccResponseCurve"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The curve to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteResponseCurve(IccResponseCurve value) |
|||
{ |
|||
int count = this.WriteUInt32((uint)value.CurveType); |
|||
int channels = value.XyzValues.Length; |
|||
|
|||
foreach (IccResponseNumber[] responseArray in value.ResponseArrays) |
|||
{ |
|||
count += this.WriteUInt32((uint)responseArray.Length); |
|||
} |
|||
|
|||
foreach (Vector3 xyz in value.XyzValues) |
|||
{ |
|||
count += this.WriteXYZNumber(xyz); |
|||
} |
|||
|
|||
foreach (IccResponseNumber[] responseArray in value.ResponseArrays) |
|||
{ |
|||
foreach (IccResponseNumber response in responseArray) |
|||
{ |
|||
count += this.WriteResponseNumber(response); |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccParametricCurve"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The curve to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteParametricCurve(IccParametricCurve value) |
|||
{ |
|||
ushort typeValue = (ushort)value.Type; |
|||
int count = this.WriteUInt16(typeValue); |
|||
count += this.WriteEmpty(2); |
|||
|
|||
if (typeValue >= 0 && typeValue <= 4) |
|||
{ |
|||
count += this.WriteFix16(value.G); |
|||
} |
|||
|
|||
if (typeValue > 0 && typeValue <= 4) |
|||
{ |
|||
count += this.WriteFix16(value.A); |
|||
count += this.WriteFix16(value.B); |
|||
} |
|||
|
|||
if (typeValue > 1 && typeValue <= 4) |
|||
{ |
|||
count += this.WriteFix16(value.C); |
|||
} |
|||
|
|||
if (typeValue > 2 && typeValue <= 4) |
|||
{ |
|||
count += this.WriteFix16(value.D); |
|||
} |
|||
|
|||
if (typeValue == 4) |
|||
{ |
|||
count += this.WriteFix16(value.E); |
|||
count += this.WriteFix16(value.F); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccCurveSegment"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The curve to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCurveSegment(IccCurveSegment value) |
|||
{ |
|||
int count = this.WriteUInt32((uint)value.Signature); |
|||
count += this.WriteEmpty(4); |
|||
|
|||
switch (value.Signature) |
|||
{ |
|||
case IccCurveSegmentSignature.FormulaCurve: |
|||
return count + this.WriteFormulaCurveElement(value as IccFormulaCurveElement); |
|||
case IccCurveSegmentSignature.SampledCurve: |
|||
return count + this.WriteSampledCurveElement(value as IccSampledCurveElement); |
|||
default: |
|||
throw new InvalidIccProfileException($"Invalid CurveSegment type of {value.Signature}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccFormulaCurveElement"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The curve to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteFormulaCurveElement(IccFormulaCurveElement value) |
|||
{ |
|||
int count = this.WriteUInt16((ushort)value.Type); |
|||
count += this.WriteEmpty(2); |
|||
|
|||
if (value.Type == IccFormulaCurveType.Type1 || value.Type == IccFormulaCurveType.Type2) |
|||
{ |
|||
count += this.WriteSingle((float)value.Gamma); |
|||
} |
|||
|
|||
count += this.WriteSingle((float)value.A); |
|||
count += this.WriteSingle((float)value.B); |
|||
count += this.WriteSingle((float)value.C); |
|||
|
|||
if (value.Type == IccFormulaCurveType.Type2 || value.Type == IccFormulaCurveType.Type3) |
|||
{ |
|||
count += this.WriteSingle((float)value.D); |
|||
} |
|||
|
|||
if (value.Type == IccFormulaCurveType.Type3) |
|||
{ |
|||
count += this.WriteSingle((float)value.E); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccSampledCurveElement"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The curve to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteSampledCurveElement(IccSampledCurveElement value) |
|||
{ |
|||
int count = this.WriteUInt32((uint)value.CurveEntries.Length); |
|||
foreach (double entry in value.CurveEntries) |
|||
{ |
|||
count += this.WriteSingle((float)entry); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
// <copyright file="IccDataWriter.Lut.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes an 8bit lookup table
|
|||
/// </summary>
|
|||
/// <param name="value">The LUT to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteLUT8(IccLut value) |
|||
{ |
|||
foreach (double item in value.Values) |
|||
{ |
|||
this.WriteByte((byte)((item * byte.MaxValue) + 0.5f).Clamp(0, byte.MaxValue)); |
|||
} |
|||
|
|||
return value.Values.Length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an 16bit lookup table
|
|||
/// </summary>
|
|||
/// <param name="value">The LUT to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteLUT16(IccLut value) |
|||
{ |
|||
foreach (double item in value.Values) |
|||
{ |
|||
this.WriteUInt16((ushort)((item * ushort.MaxValue) + 0.5f).Clamp(0, ushort.MaxValue)); |
|||
} |
|||
|
|||
return value.Values.Length * 2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an color lookup table
|
|||
/// </summary>
|
|||
/// <param name="value">The CLUT to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCLUT(IccClut value) |
|||
{ |
|||
int count = this.WriteArray(value.GridPointCount); |
|||
count += this.WriteEmpty(16 - value.GridPointCount.Length); |
|||
|
|||
switch (value.DataType) |
|||
{ |
|||
case IccClutDataType.Float: |
|||
return count + this.WriteCLUTf32(value); |
|||
case IccClutDataType.UInt8: |
|||
count += this.WriteByte(1); |
|||
count += this.WriteEmpty(3); |
|||
return count + this.WriteCLUT8(value); |
|||
case IccClutDataType.UInt16: |
|||
count += this.WriteByte(2); |
|||
count += this.WriteEmpty(3); |
|||
return count + this.WriteCLUT16(value); |
|||
|
|||
default: |
|||
throw new InvalidIccProfileException($"Invalid CLUT data type of {value.DataType}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a 8bit color lookup table
|
|||
/// </summary>
|
|||
/// <param name="value">The CLUT to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCLUT8(IccClut value) |
|||
{ |
|||
int count = 0; |
|||
foreach (float[] inArray in value.Values) |
|||
{ |
|||
foreach (float item in inArray) |
|||
{ |
|||
count += this.WriteByte((byte)((item * byte.MaxValue) + 0.5f).Clamp(0, byte.MaxValue)); |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a 16bit color lookup table
|
|||
/// </summary>
|
|||
/// <param name="value">The CLUT to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCLUT16(IccClut value) |
|||
{ |
|||
int count = 0; |
|||
foreach (float[] inArray in value.Values) |
|||
{ |
|||
foreach (float item in inArray) |
|||
{ |
|||
count += this.WriteUInt16((ushort)((item * ushort.MaxValue) + 0.5f).Clamp(0, ushort.MaxValue)); |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a 32bit float color lookup table
|
|||
/// </summary>
|
|||
/// <param name="value">The CLUT to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCLUTf32(IccClut value) |
|||
{ |
|||
int count = 0; |
|||
foreach (float[] inArray in value.Values) |
|||
{ |
|||
foreach (float item in inArray) |
|||
{ |
|||
count += this.WriteSingle(item); |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
// <copyright file="IccDataWriter.Matrix.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes a two dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="value">The matrix to write</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMatrix(Matrix4x4 value, bool isSingle) |
|||
{ |
|||
int count = 0; |
|||
|
|||
if (isSingle) |
|||
{ |
|||
count += this.WriteSingle(value.M11); |
|||
count += this.WriteSingle(value.M21); |
|||
count += this.WriteSingle(value.M31); |
|||
|
|||
count += this.WriteSingle(value.M12); |
|||
count += this.WriteSingle(value.M22); |
|||
count += this.WriteSingle(value.M32); |
|||
|
|||
count += this.WriteSingle(value.M13); |
|||
count += this.WriteSingle(value.M23); |
|||
count += this.WriteSingle(value.M33); |
|||
} |
|||
else |
|||
{ |
|||
count += this.WriteFix16(value.M11); |
|||
count += this.WriteFix16(value.M21); |
|||
count += this.WriteFix16(value.M31); |
|||
|
|||
count += this.WriteFix16(value.M12); |
|||
count += this.WriteFix16(value.M22); |
|||
count += this.WriteFix16(value.M32); |
|||
|
|||
count += this.WriteFix16(value.M13); |
|||
count += this.WriteFix16(value.M23); |
|||
count += this.WriteFix16(value.M33); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a two dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="value">The matrix to write</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMatrix(Fast2DArray<float> value, bool isSingle) |
|||
{ |
|||
int count = 0; |
|||
for (int y = 0; y < value.Height; y++) |
|||
{ |
|||
for (int x = 0; x < value.Width; x++) |
|||
{ |
|||
if (isSingle) |
|||
{ |
|||
count += this.WriteSingle(value[x, y]); |
|||
} |
|||
else |
|||
{ |
|||
count += this.WriteFix16(value[x, y]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a two dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="value">The matrix to write</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMatrix(float[,] value, bool isSingle) |
|||
{ |
|||
int count = 0; |
|||
for (int y = 0; y < value.GetLength(1); y++) |
|||
{ |
|||
for (int x = 0; x < value.GetLength(0); x++) |
|||
{ |
|||
if (isSingle) |
|||
{ |
|||
count += this.WriteSingle(value[x, y]); |
|||
} |
|||
else |
|||
{ |
|||
count += this.WriteFix16(value[x, y]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a one dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="value">The matrix to write</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMatrix(Vector3 value, bool isSingle) |
|||
{ |
|||
int count = 0; |
|||
if (isSingle) |
|||
{ |
|||
count += this.WriteSingle(value.X); |
|||
count += this.WriteSingle(value.X); |
|||
count += this.WriteSingle(value.X); |
|||
} |
|||
else |
|||
{ |
|||
count += this.WriteFix16(value.X); |
|||
count += this.WriteFix16(value.X); |
|||
count += this.WriteFix16(value.X); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a one dimensional matrix
|
|||
/// </summary>
|
|||
/// <param name="value">The matrix to write</param>
|
|||
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMatrix(float[] value, bool isSingle) |
|||
{ |
|||
int count = 0; |
|||
for (int i = 0; i < value.Length; i++) |
|||
{ |
|||
if (isSingle) |
|||
{ |
|||
count += this.WriteSingle(value[i]); |
|||
} |
|||
else |
|||
{ |
|||
count += this.WriteFix16(value[i]); |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
// <copyright file="IccDataWriter.MultiProcessElement.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes a <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The element to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMultiProcessElement(IccMultiProcessElement value) |
|||
{ |
|||
int count = this.WriteUInt32((uint)value.Signature); |
|||
count += this.WriteUInt16((ushort)value.InputChannelCount); |
|||
count += this.WriteUInt16((ushort)value.OutputChannelCount); |
|||
|
|||
switch (value.Signature) |
|||
{ |
|||
case IccMultiProcessElementSignature.CurveSet: |
|||
return count + this.WriteCurveSetProcessElement(value as IccCurveSetProcessElement); |
|||
case IccMultiProcessElementSignature.Matrix: |
|||
return count + this.WriteMatrixProcessElement(value as IccMatrixProcessElement); |
|||
case IccMultiProcessElementSignature.Clut: |
|||
return count + this.WriteCLUTProcessElement(value as IccClutProcessElement); |
|||
|
|||
case IccMultiProcessElementSignature.BAcs: |
|||
case IccMultiProcessElementSignature.EAcs: |
|||
return count + this.WriteEmpty(8); |
|||
|
|||
default: |
|||
throw new InvalidIccProfileException($"Invalid MultiProcessElement type of {value.Signature}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a CurveSet <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The element to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCurveSetProcessElement(IccCurveSetProcessElement value) |
|||
{ |
|||
int count = 0; |
|||
foreach (IccOneDimensionalCurve curve in value.Curves) |
|||
{ |
|||
count += this.WriteOneDimensionalCurve(curve); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a Matrix <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The element to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMatrixProcessElement(IccMatrixProcessElement value) |
|||
{ |
|||
return this.WriteMatrix(value.MatrixIxO, true) |
|||
+ this.WriteMatrix(value.MatrixOx1, true); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a CLUT <see cref="IccMultiProcessElement"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The element to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCLUTProcessElement(IccClutProcessElement value) |
|||
{ |
|||
return this.WriteCLUT(value.ClutValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
// <copyright file="IccDataWriter.NonPrimitives.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes a DateTime
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteDateTime(DateTime value) |
|||
{ |
|||
return this.WriteUInt16((ushort)value.Year) |
|||
+ this.WriteUInt16((ushort)value.Month) |
|||
+ this.WriteUInt16((ushort)value.Day) |
|||
+ this.WriteUInt16((ushort)value.Hour) |
|||
+ this.WriteUInt16((ushort)value.Minute) |
|||
+ this.WriteUInt16((ushort)value.Second); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an ICC profile version number
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteVersionNumber(Version value) |
|||
{ |
|||
int major = value.Major.Clamp(0, byte.MaxValue); |
|||
int minor = value.Minor.Clamp(0, 15); |
|||
int bugfix = value.Build.Clamp(0, 15); |
|||
byte mb = (byte)((minor << 4) | bugfix); |
|||
|
|||
int version = (major << 24) | (minor << 20) | (bugfix << 16); |
|||
return this.WriteInt32(version); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an XYZ number
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteXYZNumber(Vector3 value) |
|||
{ |
|||
return this.WriteFix16(value.X) |
|||
+ this.WriteFix16(value.Y) |
|||
+ this.WriteFix16(value.Z); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a profile ID
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteProfileId(IccProfileId value) |
|||
{ |
|||
return this.WriteUInt32(value.Part1) |
|||
+ this.WriteUInt32(value.Part2) |
|||
+ this.WriteUInt32(value.Part3) |
|||
+ this.WriteUInt32(value.Part4); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a position number
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WritePositionNumber(IccPositionNumber value) |
|||
{ |
|||
return this.WriteUInt32(value.Offset) |
|||
+ this.WriteUInt32(value.Size); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a response number
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteResponseNumber(IccResponseNumber value) |
|||
{ |
|||
return this.WriteUInt16(value.DeviceCode) |
|||
+ this.WriteFix16(value.MeasurementValue); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a named color
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteNamedColor(IccNamedColor value) |
|||
{ |
|||
return this.WriteASCIIString(value.Name, 32, '\0') |
|||
+ this.WriteArray(value.PcsCoordinates) |
|||
+ this.WriteArray(value.DeviceCoordinates); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a profile description
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteProfileDescription(IccProfileDescription value) |
|||
{ |
|||
return this.WriteUInt32(value.DeviceManufacturer) |
|||
+ this.WriteUInt32(value.DeviceModel) |
|||
+ this.WriteInt64((long)value.DeviceAttributes) |
|||
+ this.WriteUInt32((uint)value.TechnologyInformation) |
|||
+ this.WriteTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode) |
|||
+ this.WriteMultiLocalizedUnicodeTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.DeviceManufacturerInfo)) |
|||
+ this.WriteTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode) |
|||
+ this.WriteMultiLocalizedUnicodeTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.DeviceModelInfo)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,217 @@ |
|||
// <copyright file="IccDataWriter.Primitives.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Text; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes a byte
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteByte(byte value) |
|||
{ |
|||
this.dataStream.WriteByte(value); |
|||
return 1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an ushort
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteUInt16(ushort value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a short
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteInt16(short value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an uint
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteUInt32(uint value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 4); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an int
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteInt32(int value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 4); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an ulong
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteUInt64(ulong value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 8); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a long
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteInt64(long value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 8); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a float
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteSingle(float value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 4); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a double
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public unsafe int WriteDouble(double value) |
|||
{ |
|||
return this.WriteBytes((byte*)&value, 8); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a signed 32bit number with 1 sign bit, 15 value bits and 16 fractional bits
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteFix16(double value) |
|||
{ |
|||
const double max = short.MaxValue + (65535d / 65536d); |
|||
const double min = short.MinValue; |
|||
|
|||
value = value.Clamp(min, max); |
|||
value *= 65536d; |
|||
|
|||
return this.WriteInt32((int)Math.Round(value, MidpointRounding.AwayFromZero)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an unsigned 32bit number with 16 value bits and 16 fractional bits
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteUFix16(double value) |
|||
{ |
|||
const double max = ushort.MaxValue + (65535d / 65536d); |
|||
const double min = ushort.MinValue; |
|||
|
|||
value = value.Clamp(min, max); |
|||
value *= 65536d; |
|||
|
|||
return this.WriteUInt32((uint)Math.Round(value, MidpointRounding.AwayFromZero)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an unsigned 16bit number with 1 value bit and 15 fractional bits
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteU1Fix15(double value) |
|||
{ |
|||
const double max = 1 + (32767d / 32768d); |
|||
const double min = 0; |
|||
|
|||
value = value.Clamp(min, max); |
|||
value *= 32768d; |
|||
|
|||
return this.WriteUInt16((ushort)Math.Round(value, MidpointRounding.AwayFromZero)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an unsigned 16bit number with 8 value bits and 8 fractional bits
|
|||
/// </summary>
|
|||
/// <param name="value">The value to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteUFix8(double value) |
|||
{ |
|||
const double max = byte.MaxValue + (255d / 256d); |
|||
const double min = byte.MinValue; |
|||
|
|||
value = value.Clamp(min, max); |
|||
value *= 256d; |
|||
|
|||
return this.WriteUInt16((ushort)Math.Round(value, MidpointRounding.AwayFromZero)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an ASCII encoded string
|
|||
/// </summary>
|
|||
/// <param name="value">the string to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteASCIIString(string value) |
|||
{ |
|||
byte[] data = AsciiEncoding.GetBytes(value); |
|||
this.dataStream.Write(data, 0, data.Length); |
|||
return data.Length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an ASCII encoded string resizes it to the given length
|
|||
/// </summary>
|
|||
/// <param name="value">The string to write</param>
|
|||
/// <param name="length">The desired length of the string including 1 padding character</param>
|
|||
/// <param name="paddingChar">The character to pad to the given length</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteASCIIString(string value, int length, char paddingChar) |
|||
{ |
|||
value = value.Substring(0, Math.Min(length - 1, value.Length)); |
|||
|
|||
byte[] textData = AsciiEncoding.GetBytes(value); |
|||
int actualLength = Math.Min(length - 1, textData.Length); |
|||
this.dataStream.Write(textData, 0, actualLength); |
|||
for (int i = 0; i < length - actualLength; i++) |
|||
{ |
|||
this.dataStream.WriteByte((byte)paddingChar); |
|||
} |
|||
|
|||
return length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an UTF-16 big-endian encoded string
|
|||
/// </summary>
|
|||
/// <param name="value">the string to write</param>
|
|||
/// <returns>the number of bytes written</returns>
|
|||
public int WriteUnicodeString(string value) |
|||
{ |
|||
byte[] data = Encoding.BigEndianUnicode.GetBytes(value); |
|||
this.dataStream.Write(data, 0, data.Length); |
|||
return data.Length; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,913 @@ |
|||
// <copyright file="IccDataWriter.TagDataEntry.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes a tag data entry
|
|||
/// </summary>
|
|||
/// <param name="data">The entry to write</param>
|
|||
/// <param name="table">The table entry for the written data entry</param>
|
|||
/// <returns>The number of bytes written (excluding padding)</returns>
|
|||
public int WriteTagDataEntry(IccTagDataEntry data, out IccTagTableEntry table) |
|||
{ |
|||
uint offset = (uint)this.dataStream.Position; |
|||
int count = this.WriteTagDataEntry(data); |
|||
this.WritePadding(); |
|||
table = new IccTagTableEntry(data.TagSignature, offset, (uint)count); |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a tag data entry (without padding)
|
|||
/// </summary>
|
|||
/// <param name="entry">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteTagDataEntry(IccTagDataEntry entry) |
|||
{ |
|||
int count = this.WriteTagDataEntryHeader(entry.Signature); |
|||
|
|||
switch (entry.Signature) |
|||
{ |
|||
case IccTypeSignature.Chromaticity: |
|||
count += this.WriteChromaticityTagDataEntry(entry as IccChromaticityTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ColorantOrder: |
|||
count += this.WriteColorantOrderTagDataEntry(entry as IccColorantOrderTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ColorantTable: |
|||
count += this.WriteColorantTableTagDataEntry(entry as IccColorantTableTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Curve: |
|||
count += this.WriteCurveTagDataEntry(entry as IccCurveTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Data: |
|||
count += this.WriteDataTagDataEntry(entry as IccDataTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.DateTime: |
|||
count += this.WriteDateTimeTagDataEntry(entry as IccDateTimeTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Lut16: |
|||
count += this.WriteLut16TagDataEntry(entry as IccLut16TagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Lut8: |
|||
count += this.WriteLut8TagDataEntry(entry as IccLut8TagDataEntry); |
|||
break; |
|||
case IccTypeSignature.LutAToB: |
|||
count += this.WriteLutAToBTagDataEntry(entry as IccLutAToBTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.LutBToA: |
|||
count += this.WriteLutBToATagDataEntry(entry as IccLutBToATagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Measurement: |
|||
count += this.WriteMeasurementTagDataEntry(entry as IccMeasurementTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.MultiLocalizedUnicode: |
|||
count += this.WriteMultiLocalizedUnicodeTagDataEntry(entry as IccMultiLocalizedUnicodeTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.MultiProcessElements: |
|||
count += this.WriteMultiProcessElementsTagDataEntry(entry as IccMultiProcessElementsTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.NamedColor2: |
|||
count += this.WriteNamedColor2TagDataEntry(entry as IccNamedColor2TagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ParametricCurve: |
|||
count += this.WriteParametricCurveTagDataEntry(entry as IccParametricCurveTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ProfileSequenceDesc: |
|||
count += this.WriteProfileSequenceDescTagDataEntry(entry as IccProfileSequenceDescTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ProfileSequenceIdentifier: |
|||
count += this.WriteProfileSequenceIdentifierTagDataEntry(entry as IccProfileSequenceIdentifierTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ResponseCurveSet16: |
|||
count += this.WriteResponseCurveSet16TagDataEntry(entry as IccResponseCurveSet16TagDataEntry); |
|||
break; |
|||
case IccTypeSignature.S15Fixed16Array: |
|||
count += this.WriteFix16ArrayTagDataEntry(entry as IccFix16ArrayTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Signature: |
|||
count += this.WriteSignatureTagDataEntry(entry as IccSignatureTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Text: |
|||
count += this.WriteTextTagDataEntry(entry as IccTextTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.U16Fixed16Array: |
|||
count += this.WriteUFix16ArrayTagDataEntry(entry as IccUFix16ArrayTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.UInt16Array: |
|||
count += this.WriteUInt16ArrayTagDataEntry(entry as IccUInt16ArrayTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.UInt32Array: |
|||
count += this.WriteUInt32ArrayTagDataEntry(entry as IccUInt32ArrayTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.UInt64Array: |
|||
count += this.WriteUInt64ArrayTagDataEntry(entry as IccUInt64ArrayTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.UInt8Array: |
|||
count += this.WriteUInt8ArrayTagDataEntry(entry as IccUInt8ArrayTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.ViewingConditions: |
|||
count += this.WriteViewingConditionsTagDataEntry(entry as IccViewingConditionsTagDataEntry); |
|||
break; |
|||
case IccTypeSignature.Xyz: |
|||
count += this.WriteXyzTagDataEntry(entry as IccXyzTagDataEntry); |
|||
break; |
|||
|
|||
// V2 Type:
|
|||
case IccTypeSignature.TextDescription: |
|||
count += this.WriteTextDescriptionTagDataEntry(entry as IccTextDescriptionTagDataEntry); |
|||
break; |
|||
|
|||
case IccTypeSignature.Unknown: |
|||
default: |
|||
count += this.WriteUnknownTagDataEntry(entry as IccUnknownTagDataEntry); |
|||
break; |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the header of a <see cref="IccTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="signature">The signature of the entry</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteTagDataEntryHeader(IccTypeSignature signature) |
|||
{ |
|||
return this.WriteUInt32((uint)signature) |
|||
+ this.WriteEmpty(4); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccUnknownTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteUnknownTagDataEntry(IccUnknownTagDataEntry value) |
|||
{ |
|||
return this.WriteArray(value.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccChromaticityTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteChromaticityTagDataEntry(IccChromaticityTagDataEntry value) |
|||
{ |
|||
int count = this.WriteUInt16((ushort)value.ChannelCount); |
|||
count += this.WriteUInt16((ushort)value.ColorantType); |
|||
|
|||
for (int i = 0; i < value.ChannelCount; i++) |
|||
{ |
|||
count += this.WriteUFix16(value.ChannelValues[i][0]); |
|||
count += this.WriteUFix16(value.ChannelValues[i][1]); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccColorantOrderTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteColorantOrderTagDataEntry(IccColorantOrderTagDataEntry value) |
|||
{ |
|||
return this.WriteUInt32((uint)value.ColorantNumber.Length) |
|||
+ this.WriteArray(value.ColorantNumber); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccColorantTableTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteColorantTableTagDataEntry(IccColorantTableTagDataEntry value) |
|||
{ |
|||
int count = this.WriteUInt32((uint)value.ColorantData.Length); |
|||
foreach (IccColorantTableEntry colorant in value.ColorantData) |
|||
{ |
|||
count += this.WriteASCIIString(colorant.Name, 32, '\0'); |
|||
count += this.WriteUInt16(colorant.Pcs1); |
|||
count += this.WriteUInt16(colorant.Pcs2); |
|||
count += this.WriteUInt16(colorant.Pcs3); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccCurveTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteCurveTagDataEntry(IccCurveTagDataEntry value) |
|||
{ |
|||
int count = 0; |
|||
|
|||
if (value.IsIdentityResponse) |
|||
{ |
|||
count += this.WriteUInt32(0); |
|||
} |
|||
else if (value.IsGamma) |
|||
{ |
|||
count += this.WriteUInt32(1); |
|||
count += this.WriteUFix8(value.Gamma); |
|||
} |
|||
else |
|||
{ |
|||
count += this.WriteUInt32((uint)value.CurveData.Length); |
|||
for (int i = 0; i < value.CurveData.Length; i++) |
|||
{ |
|||
count += this.WriteUInt16((ushort)((value.CurveData[i] * ushort.MaxValue) + 0.5f).Clamp(0, ushort.MaxValue)); |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
|
|||
// TODO: Page 48: If the input is PCSXYZ, 1+(32 767/32 768) shall be mapped to the value 1,0. If the output is PCSXYZ, the value 1,0 shall be mapped to 1+(32 767/32 768).
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccDataTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteDataTagDataEntry(IccDataTagDataEntry value) |
|||
{ |
|||
return this.WriteEmpty(3) |
|||
+ this.WriteByte((byte)(value.IsAscii ? 0x01 : 0x00)) |
|||
+ this.WriteArray(value.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccDateTimeTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteDateTimeTagDataEntry(IccDateTimeTagDataEntry value) |
|||
{ |
|||
return this.WriteDateTime(value.Value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccLut16TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteLut16TagDataEntry(IccLut16TagDataEntry value) |
|||
{ |
|||
int count = this.WriteByte((byte)value.InputValues.Length); |
|||
count += this.WriteByte((byte)value.OutputValues.Length); |
|||
count += this.WriteByte(value.ClutValues.GridPointCount[0]); |
|||
count += this.WriteEmpty(1); |
|||
|
|||
count += this.WriteMatrix(value.Matrix, false); |
|||
|
|||
count += this.WriteUInt16((ushort)value.InputValues[0].Values.Length); |
|||
count += this.WriteUInt16((ushort)value.OutputValues[0].Values.Length); |
|||
|
|||
foreach (IccLut lut in value.InputValues) |
|||
{ |
|||
count += this.WriteLUT16(lut); |
|||
} |
|||
|
|||
count += this.WriteCLUT16(value.ClutValues); |
|||
|
|||
foreach (IccLut lut in value.OutputValues) |
|||
{ |
|||
count += this.WriteLUT16(lut); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccLut8TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteLut8TagDataEntry(IccLut8TagDataEntry value) |
|||
{ |
|||
int count = this.WriteByte((byte)value.InputChannelCount); |
|||
count += this.WriteByte((byte)value.OutputChannelCount); |
|||
count += this.WriteByte((byte)value.ClutValues.Values[0].Length); |
|||
count += this.WriteEmpty(1); |
|||
|
|||
count += this.WriteMatrix(value.Matrix, false); |
|||
|
|||
foreach (IccLut lut in value.InputValues) |
|||
{ |
|||
count += this.WriteLUT8(lut); |
|||
} |
|||
|
|||
count += this.WriteCLUT8(value.ClutValues); |
|||
|
|||
foreach (IccLut lut in value.OutputValues) |
|||
{ |
|||
count += this.WriteLUT8(lut); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccLutAToBTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteLutAToBTagDataEntry(IccLutAToBTagDataEntry value) |
|||
{ |
|||
long start = this.dataStream.Position - 8; // 8 is the tag header size
|
|||
|
|||
int count = this.WriteByte((byte)value.InputChannelCount); |
|||
count += this.WriteByte((byte)value.OutputChannelCount); |
|||
count += this.WriteEmpty(2); |
|||
|
|||
long bCurveOffset = 0; |
|||
long matrixOffset = 0; |
|||
long mCurveOffset = 0; |
|||
long clutOffset = 0; |
|||
long aCurveOffset = 0; |
|||
|
|||
// Jump over offset values
|
|||
long offsetpos = this.dataStream.Position; |
|||
this.dataStream.Position += 5 * 4; |
|||
|
|||
if (value.CurveB != null) |
|||
{ |
|||
bCurveOffset = this.dataStream.Position; |
|||
count += this.WriteCurves(value.CurveB); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.Matrix3x1 != null && value.Matrix3x3 != null) |
|||
{ |
|||
matrixOffset = this.dataStream.Position; |
|||
count += this.WriteMatrix(value.Matrix3x3.Value, false); |
|||
count += this.WriteMatrix(value.Matrix3x1.Value, false); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.CurveM != null) |
|||
{ |
|||
mCurveOffset = this.dataStream.Position; |
|||
count += this.WriteCurves(value.CurveM); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.ClutValues != null) |
|||
{ |
|||
clutOffset = this.dataStream.Position; |
|||
count += this.WriteCLUT(value.ClutValues); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.CurveA != null) |
|||
{ |
|||
aCurveOffset = this.dataStream.Position; |
|||
count += this.WriteCurves(value.CurveA); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
// Set offset values
|
|||
long lpos = this.dataStream.Position; |
|||
this.dataStream.Position = offsetpos; |
|||
|
|||
if (bCurveOffset != 0) |
|||
{ |
|||
bCurveOffset -= start; |
|||
} |
|||
|
|||
if (matrixOffset != 0) |
|||
{ |
|||
matrixOffset -= start; |
|||
} |
|||
|
|||
if (mCurveOffset != 0) |
|||
{ |
|||
mCurveOffset -= start; |
|||
} |
|||
|
|||
if (clutOffset != 0) |
|||
{ |
|||
clutOffset -= start; |
|||
} |
|||
|
|||
if (aCurveOffset != 0) |
|||
{ |
|||
aCurveOffset -= start; |
|||
} |
|||
|
|||
count += this.WriteUInt32((uint)bCurveOffset); |
|||
count += this.WriteUInt32((uint)matrixOffset); |
|||
count += this.WriteUInt32((uint)mCurveOffset); |
|||
count += this.WriteUInt32((uint)clutOffset); |
|||
count += this.WriteUInt32((uint)aCurveOffset); |
|||
|
|||
this.dataStream.Position = lpos; |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccLutBToATagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteLutBToATagDataEntry(IccLutBToATagDataEntry value) |
|||
{ |
|||
long start = this.dataStream.Position - 8; // 8 is the tag header size
|
|||
|
|||
int count = this.WriteByte((byte)value.InputChannelCount); |
|||
count += this.WriteByte((byte)value.OutputChannelCount); |
|||
count += this.WriteEmpty(2); |
|||
|
|||
long bCurveOffset = 0; |
|||
long matrixOffset = 0; |
|||
long mCurveOffset = 0; |
|||
long clutOffset = 0; |
|||
long aCurveOffset = 0; |
|||
|
|||
// Jump over offset values
|
|||
long offsetpos = this.dataStream.Position; |
|||
this.dataStream.Position += 5 * 4; |
|||
|
|||
if (value.CurveB != null) |
|||
{ |
|||
bCurveOffset = this.dataStream.Position; |
|||
count += this.WriteCurves(value.CurveB); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.Matrix3x1 != null && value.Matrix3x3 != null) |
|||
{ |
|||
matrixOffset = this.dataStream.Position; |
|||
count += this.WriteMatrix(value.Matrix3x3.Value, false); |
|||
count += this.WriteMatrix(value.Matrix3x1.Value, false); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.CurveM != null) |
|||
{ |
|||
mCurveOffset = this.dataStream.Position; |
|||
count += this.WriteCurves(value.CurveM); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.ClutValues != null) |
|||
{ |
|||
clutOffset = this.dataStream.Position; |
|||
count += this.WriteCLUT(value.ClutValues); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
if (value.CurveA != null) |
|||
{ |
|||
aCurveOffset = this.dataStream.Position; |
|||
count += this.WriteCurves(value.CurveA); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
// Set offset values
|
|||
long lpos = this.dataStream.Position; |
|||
this.dataStream.Position = offsetpos; |
|||
|
|||
if (bCurveOffset != 0) |
|||
{ |
|||
bCurveOffset -= start; |
|||
} |
|||
|
|||
if (matrixOffset != 0) |
|||
{ |
|||
matrixOffset -= start; |
|||
} |
|||
|
|||
if (mCurveOffset != 0) |
|||
{ |
|||
mCurveOffset -= start; |
|||
} |
|||
|
|||
if (clutOffset != 0) |
|||
{ |
|||
clutOffset -= start; |
|||
} |
|||
|
|||
if (aCurveOffset != 0) |
|||
{ |
|||
aCurveOffset -= start; |
|||
} |
|||
|
|||
count += this.WriteUInt32((uint)bCurveOffset); |
|||
count += this.WriteUInt32((uint)matrixOffset); |
|||
count += this.WriteUInt32((uint)mCurveOffset); |
|||
count += this.WriteUInt32((uint)clutOffset); |
|||
count += this.WriteUInt32((uint)aCurveOffset); |
|||
|
|||
this.dataStream.Position = lpos; |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccMeasurementTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMeasurementTagDataEntry(IccMeasurementTagDataEntry value) |
|||
{ |
|||
return this.WriteUInt32((uint)value.Observer) |
|||
+ this.WriteXYZNumber(value.XyzBacking) |
|||
+ this.WriteUInt32((uint)value.Geometry) |
|||
+ this.WriteUFix16(value.Flare) |
|||
+ this.WriteUInt32((uint)value.Illuminant); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccMultiLocalizedUnicodeTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMultiLocalizedUnicodeTagDataEntry(IccMultiLocalizedUnicodeTagDataEntry value) |
|||
{ |
|||
long start = this.dataStream.Position - 8; // 8 is the tag header size
|
|||
|
|||
int cultureCount = value.Texts.Length; |
|||
|
|||
int count = this.WriteUInt32((uint)cultureCount); |
|||
count += this.WriteUInt32(12); // One record has always 12 bytes size
|
|||
|
|||
// Jump over position table
|
|||
long tpos = this.dataStream.Position; |
|||
this.dataStream.Position += cultureCount * 12; |
|||
|
|||
uint[] offset = new uint[cultureCount]; |
|||
int[] lengths = new int[cultureCount]; |
|||
|
|||
for (int i = 0; i < cultureCount; i++) |
|||
{ |
|||
offset[i] = (uint)(this.dataStream.Position - start); |
|||
count += lengths[i] = this.WriteUnicodeString(value.Texts[i].Text); |
|||
} |
|||
|
|||
// Write position table
|
|||
long lpos = this.dataStream.Position; |
|||
this.dataStream.Position = tpos; |
|||
for (int i = 0; i < cultureCount; i++) |
|||
{ |
|||
string[] code = value.Texts[i].Culture.Name.Split('-'); |
|||
|
|||
count += this.WriteASCIIString(code[0].ToLower(), 2, ' '); |
|||
count += this.WriteASCIIString(code[1].ToUpper(), 2, ' '); |
|||
|
|||
count += this.WriteUInt32((uint)lengths[i]); |
|||
count += this.WriteUInt32(offset[i]); |
|||
} |
|||
|
|||
this.dataStream.Position = lpos; |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccMultiProcessElementsTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteMultiProcessElementsTagDataEntry(IccMultiProcessElementsTagDataEntry value) |
|||
{ |
|||
long start = this.dataStream.Position - 8; // 8 is the tag header size
|
|||
|
|||
int count = this.WriteUInt16((ushort)value.InputChannelCount); |
|||
count += this.WriteUInt16((ushort)value.OutputChannelCount); |
|||
count += this.WriteUInt32((uint)value.Data.Length); |
|||
|
|||
// Jump over position table
|
|||
long tpos = this.dataStream.Position; |
|||
this.dataStream.Position += value.Data.Length * 8; |
|||
|
|||
IccPositionNumber[] posTable = new IccPositionNumber[value.Data.Length]; |
|||
for (int i = 0; i < value.Data.Length; i++) |
|||
{ |
|||
uint offset = (uint)(this.dataStream.Position - start); |
|||
int size = this.WriteMultiProcessElement(value.Data[i]); |
|||
count += this.WritePadding(); |
|||
posTable[i] = new IccPositionNumber(offset, (uint)size); |
|||
count += size; |
|||
} |
|||
|
|||
// Write position table
|
|||
long lpos = this.dataStream.Position; |
|||
this.dataStream.Position = tpos; |
|||
foreach (IccPositionNumber pos in posTable) |
|||
{ |
|||
count += this.WritePositionNumber(pos); |
|||
} |
|||
|
|||
this.dataStream.Position = lpos; |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccNamedColor2TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteNamedColor2TagDataEntry(IccNamedColor2TagDataEntry value) |
|||
{ |
|||
int count = this.WriteInt32(value.VendorFlags) |
|||
+ this.WriteUInt32((uint)value.Colors.Length) |
|||
+ this.WriteUInt32((uint)value.CoordinateCount) |
|||
+ this.WriteASCIIString(value.Prefix, 32, '\0') |
|||
+ this.WriteASCIIString(value.Suffix, 32, '\0'); |
|||
|
|||
foreach (IccNamedColor color in value.Colors) |
|||
{ |
|||
count += this.WriteNamedColor(color); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccParametricCurveTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteParametricCurveTagDataEntry(IccParametricCurveTagDataEntry value) |
|||
{ |
|||
return this.WriteParametricCurve(value.Curve); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccProfileSequenceDescTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteProfileSequenceDescTagDataEntry(IccProfileSequenceDescTagDataEntry value) |
|||
{ |
|||
int count = this.WriteUInt32((uint)value.Descriptions.Length); |
|||
foreach (IccProfileDescription desc in value.Descriptions) |
|||
{ |
|||
count += this.WriteProfileDescription(desc); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccProfileSequenceIdentifierTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteProfileSequenceIdentifierTagDataEntry(IccProfileSequenceIdentifierTagDataEntry value) |
|||
{ |
|||
long start = this.dataStream.Position - 8; // 8 is the tag header size
|
|||
int length = value.Data.Length; |
|||
|
|||
int count = this.WriteUInt32((uint)length); |
|||
|
|||
// Jump over position table
|
|||
long tablePosition = this.dataStream.Position; |
|||
this.dataStream.Position += length * 8; |
|||
IccPositionNumber[] table = new IccPositionNumber[length]; |
|||
|
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
uint offset = (uint)(this.dataStream.Position - start); |
|||
int size = this.WriteProfileId(value.Data[i].Id); |
|||
size += this.WriteTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.Data[i].Description)); |
|||
size += this.WritePadding(); |
|||
table[i] = new IccPositionNumber(offset, (uint)size); |
|||
count += size; |
|||
} |
|||
|
|||
// Write position table
|
|||
long lpos = this.dataStream.Position; |
|||
this.dataStream.Position = tablePosition; |
|||
foreach (IccPositionNumber pos in table) |
|||
{ |
|||
count += this.WritePositionNumber(pos); |
|||
} |
|||
|
|||
this.dataStream.Position = lpos; |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccResponseCurveSet16TagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteResponseCurveSet16TagDataEntry(IccResponseCurveSet16TagDataEntry value) |
|||
{ |
|||
long start = this.dataStream.Position - 8; |
|||
|
|||
int count = this.WriteUInt16(value.ChannelCount); |
|||
count += this.WriteUInt16((ushort)value.Curves.Length); |
|||
|
|||
// Jump over position table
|
|||
long tablePosition = this.dataStream.Position; |
|||
this.dataStream.Position += value.Curves.Length * 4; |
|||
|
|||
uint[] offset = new uint[value.Curves.Length]; |
|||
|
|||
for (int i = 0; i < value.Curves.Length; i++) |
|||
{ |
|||
offset[i] = (uint)(this.dataStream.Position - start); |
|||
count += this.WriteResponseCurve(value.Curves[i]); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
// Write position table
|
|||
long lpos = this.dataStream.Position; |
|||
this.dataStream.Position = tablePosition; |
|||
count += this.WriteArray(offset); |
|||
|
|||
this.dataStream.Position = lpos; |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccFix16ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteFix16ArrayTagDataEntry(IccFix16ArrayTagDataEntry value) |
|||
{ |
|||
int count = 0; |
|||
for (int i = 0; i < value.Data.Length; i++) |
|||
{ |
|||
count += this.WriteFix16(value.Data[i] * 256d); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccSignatureTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteSignatureTagDataEntry(IccSignatureTagDataEntry value) |
|||
{ |
|||
return this.WriteASCIIString(value.SignatureData, 4, ' '); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccTextTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteTextTagDataEntry(IccTextTagDataEntry value) |
|||
{ |
|||
return this.WriteASCIIString(value.Text); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccUFix16ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteUFix16ArrayTagDataEntry(IccUFix16ArrayTagDataEntry value) |
|||
{ |
|||
int count = 0; |
|||
for (int i = 0; i < value.Data.Length; i++) |
|||
{ |
|||
count += this.WriteUFix16(value.Data[i]); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccUInt16ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteUInt16ArrayTagDataEntry(IccUInt16ArrayTagDataEntry value) |
|||
{ |
|||
return this.WriteArray(value.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccUInt32ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteUInt32ArrayTagDataEntry(IccUInt32ArrayTagDataEntry value) |
|||
{ |
|||
return this.WriteArray(value.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccUInt64ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteUInt64ArrayTagDataEntry(IccUInt64ArrayTagDataEntry value) |
|||
{ |
|||
return this.WriteArray(value.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccUInt8ArrayTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteUInt8ArrayTagDataEntry(IccUInt8ArrayTagDataEntry value) |
|||
{ |
|||
return this.WriteArray(value.Data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccViewingConditionsTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteViewingConditionsTagDataEntry(IccViewingConditionsTagDataEntry value) |
|||
{ |
|||
return this.WriteXYZNumber(value.IlluminantXyz) |
|||
+ this.WriteXYZNumber(value.SurroundXyz) |
|||
+ this.WriteUInt32((uint)value.Illuminant); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccXyzTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteXyzTagDataEntry(IccXyzTagDataEntry value) |
|||
{ |
|||
int count = 0; |
|||
for (int i = 0; i < value.Data.Length; i++) |
|||
{ |
|||
count += this.WriteXYZNumber(value.Data[i]); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a <see cref="IccTextDescriptionTagDataEntry"/>
|
|||
/// </summary>
|
|||
/// <param name="value">The entry to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteTextDescriptionTagDataEntry(IccTextDescriptionTagDataEntry value) |
|||
{ |
|||
int size, count = 0; |
|||
|
|||
if (value.Ascii == null) |
|||
{ |
|||
count += this.WriteUInt32(0); |
|||
} |
|||
else |
|||
{ |
|||
this.dataStream.Position += 4; |
|||
count += size = this.WriteASCIIString(value.Ascii + '\0'); |
|||
this.dataStream.Position -= size + 4; |
|||
count += this.WriteUInt32((uint)size); |
|||
this.dataStream.Position += size; |
|||
} |
|||
|
|||
if (value.Unicode == null) |
|||
{ |
|||
count += this.WriteUInt32(0); |
|||
count += this.WriteUInt32(0); |
|||
} |
|||
else |
|||
{ |
|||
this.dataStream.Position += 8; |
|||
count += size = this.WriteUnicodeString(value.Unicode + '\0'); |
|||
this.dataStream.Position -= size + 8; |
|||
count += this.WriteUInt32(value.UnicodeLanguageCode); |
|||
count += this.WriteUInt32((uint)value.Unicode.Length + 1); |
|||
this.dataStream.Position += size; |
|||
} |
|||
|
|||
if (value.ScriptCode == null) |
|||
{ |
|||
count += this.WriteUInt16(0); |
|||
count += this.WriteByte(0); |
|||
count += this.WriteEmpty(67); |
|||
} |
|||
else |
|||
{ |
|||
this.dataStream.Position += 3; |
|||
count += size = this.WriteASCIIString(value.ScriptCode, 67, '\0'); |
|||
this.dataStream.Position -= size + 3; |
|||
count += this.WriteUInt16(value.ScriptCodeCode); |
|||
count += this.WriteByte((byte)(value.ScriptCode.Length > 66 ? 67 : value.ScriptCode.Length)); |
|||
this.dataStream.Position += size; |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,234 @@ |
|||
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods to write ICC data types
|
|||
/// </summary>
|
|||
internal sealed partial class IccDataWriter |
|||
{ |
|||
private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian; |
|||
private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); |
|||
|
|||
private static readonly double[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; |
|||
|
|||
/// <summary>
|
|||
/// The underlying stream where the data is written to
|
|||
/// </summary>
|
|||
private readonly MemoryStream dataStream; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IccDataWriter"/> class.
|
|||
/// </summary>
|
|||
public IccDataWriter() |
|||
{ |
|||
this.dataStream = new MemoryStream(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently written length in bytes
|
|||
/// </summary>
|
|||
public uint Length |
|||
{ |
|||
get { return (uint)this.dataStream.Length; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the written data bytes
|
|||
/// </summary>
|
|||
/// <returns>The written data</returns>
|
|||
public byte[] GetData() |
|||
{ |
|||
return this.dataStream.ToArray(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the writing position to the given value
|
|||
/// </summary>
|
|||
/// <param name="index">The new index position</param>
|
|||
public void SetIndex(int index) |
|||
{ |
|||
this.dataStream.Position = index; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a byte array
|
|||
/// </summary>
|
|||
/// <param name="data">The array to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteArray(byte[] data) |
|||
{ |
|||
this.dataStream.Write(data, 0, data.Length); |
|||
return data.Length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a ushort array
|
|||
/// </summary>
|
|||
/// <param name="data">The array to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteArray(ushort[] data) |
|||
{ |
|||
for (int i = 0; i < data.Length; i++) |
|||
{ |
|||
this.WriteUInt16(data[i]); |
|||
} |
|||
|
|||
return data.Length * 2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a short array
|
|||
/// </summary>
|
|||
/// <param name="data">The array to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteArray(short[] data) |
|||
{ |
|||
for (int i = 0; i < data.Length; i++) |
|||
{ |
|||
this.WriteInt16(data[i]); |
|||
} |
|||
|
|||
return data.Length * 2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a uint array
|
|||
/// </summary>
|
|||
/// <param name="data">The array to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteArray(uint[] data) |
|||
{ |
|||
for (int i = 0; i < data.Length; i++) |
|||
{ |
|||
this.WriteUInt32(data[i]); |
|||
} |
|||
|
|||
return data.Length * 4; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes an int array
|
|||
/// </summary>
|
|||
/// <param name="data">The array to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteArray(int[] data) |
|||
{ |
|||
for (int i = 0; i < data.Length; i++) |
|||
{ |
|||
this.WriteInt32(data[i]); |
|||
} |
|||
|
|||
return data.Length * 4; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a ulong array
|
|||
/// </summary>
|
|||
/// <param name="data">The array to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteArray(ulong[] data) |
|||
{ |
|||
for (int i = 0; i < data.Length; i++) |
|||
{ |
|||
this.WriteUInt64(data[i]); |
|||
} |
|||
|
|||
return data.Length * 8; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write a number of empty bytes
|
|||
/// </summary>
|
|||
/// <param name="length">The number of bytes to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WriteEmpty(int length) |
|||
{ |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
this.dataStream.WriteByte(0); |
|||
} |
|||
|
|||
return length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes empty bytes to a 4-byte margin
|
|||
/// </summary>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
public int WritePadding() |
|||
{ |
|||
int p = 4 - ((int)this.dataStream.Position % 4); |
|||
return this.WriteEmpty(p >= 4 ? 0 : p); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes given bytes from pointer
|
|||
/// </summary>
|
|||
/// <param name="data">Pointer to the bytes to write</param>
|
|||
/// <param name="length">The number of bytes to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
private unsafe int WriteBytes(byte* data, int length) |
|||
{ |
|||
if (IsLittleEndian) |
|||
{ |
|||
for (int i = length - 1; i >= 0; i--) |
|||
{ |
|||
this.dataStream.WriteByte(data[i]); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
this.WriteBytesDirect(data, length); |
|||
} |
|||
|
|||
return length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes given bytes from pointer ignoring endianness
|
|||
/// </summary>
|
|||
/// <param name="data">Pointer to the bytes to write</param>
|
|||
/// <param name="length">The number of bytes to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
private unsafe int WriteBytesDirect(byte* data, int length) |
|||
{ |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
this.dataStream.WriteByte(data[i]); |
|||
} |
|||
|
|||
return length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes curve data
|
|||
/// </summary>
|
|||
/// <param name="curves">The curves to write</param>
|
|||
/// <returns>The number of bytes written</returns>
|
|||
private int WriteCurves(IccTagDataEntry[] curves) |
|||
{ |
|||
int count = 0; |
|||
foreach (IccTagDataEntry curve in curves) |
|||
{ |
|||
if (curve.Signature != IccTypeSignature.Curve && curve.Signature != IccTypeSignature.ParametricCurve) |
|||
{ |
|||
throw new InvalidIccProfileException($"Curve has to be either \"{nameof(IccTypeSignature)}.{nameof(IccTypeSignature.Curve)}\" or" + |
|||
$" \"{nameof(IccTypeSignature)}.{nameof(IccTypeSignature.ParametricCurve)}\" for LutAToB- and LutBToA-TagDataEntries"); |
|||
} |
|||
|
|||
count += this.WriteTagDataEntry(curve); |
|||
count += this.WritePadding(); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
Loading…
Reference in new issue