Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;

namespace SixLabors.ImageSharp.ColorProfiles.Conversion.Icc;

internal partial class CurveCalculator : ISingleCalculator
{
private readonly LutCalculator lutCalculator;
private readonly LutCalculator? lutCalculator;
private readonly float gamma;
private readonly CalculationType type;

Expand All @@ -36,12 +36,26 @@ public CurveCalculator(IccCurveTagDataEntry entry, bool inverted)
}
}

[MemberNotNullWhen(true, nameof(lutCalculator))]
private bool IsLut => this.type == CalculationType.Lut;

public float Calculate(float value)
=> this.type switch
{
if (this.IsLut)
{
return this.lutCalculator.Calculate(value);
}

if (this.type == CalculationType.Gamma)
{
CalculationType.Identity => value,
CalculationType.Gamma => MathF.Pow(value, this.gamma), // TODO: This could be optimized using a LUT. See SrgbCompanding
CalculationType.Lut => this.lutCalculator.Calculate(value),
_ => throw new InvalidOperationException("Invalid calculation type"),
};
return MathF.Pow(value, this.gamma);
}

if (this.type == CalculationType.Identity)
{
return value;
}

throw new InvalidOperationException("Invalid calculation type");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal class GrayTrcCalculator : IVector4Calculator
private readonly TrcCalculator calculator;

public GrayTrcCalculator(IccTagDataEntry grayTrc, bool toPcs)
=> this.calculator = new TrcCalculator(new IccTagDataEntry[] { grayTrc }, !toPcs);
=> this.calculator = new TrcCalculator([grayTrc], !toPcs);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value) => this.calculator.Calculate(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace SixLabors.ImageSharp.ColorProfiles.Conversion.Icc;

internal partial class LutABCalculator
{
[Flags]
private enum CalculationType
{
AtoB = 1 << 3,
Expand Down
73 changes: 36 additions & 37 deletions src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Numerics;
using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators;
Expand All @@ -11,22 +10,22 @@ namespace SixLabors.ImageSharp.ColorProfiles.Conversion.Icc;
internal partial class LutABCalculator : IVector4Calculator
{
private CalculationType type;
private TrcCalculator curveACalculator;
private TrcCalculator curveBCalculator;
private TrcCalculator curveMCalculator;
private MatrixCalculator matrixCalculator;
private ClutCalculator clutCalculator;
private TrcCalculator? curveACalculator;
private TrcCalculator? curveBCalculator;
private TrcCalculator? curveMCalculator;
private MatrixCalculator? matrixCalculator;
private ClutCalculator? clutCalculator;

public LutABCalculator(IccLutAToBTagDataEntry entry)
{
Guard.NotNull(entry, nameof(entry));
Guard.NotNull(entry);
this.Init(entry.CurveA, entry.CurveB, entry.CurveM, entry.Matrix3x1, entry.Matrix3x3, entry.ClutValues);
this.type |= CalculationType.AtoB;
}

public LutABCalculator(IccLutBToATagDataEntry entry)
{
Guard.NotNull(entry, nameof(entry));
Guard.NotNull(entry);
this.Init(entry.CurveA, entry.CurveB, entry.CurveM, entry.Matrix3x1, entry.Matrix3x3, entry.ClutValues);
this.type |= CalculationType.BtoA;
}
Expand All @@ -36,49 +35,49 @@ public Vector4 Calculate(Vector4 value)
switch (this.type)
{
case CalculationType.Full | CalculationType.AtoB:
value = this.curveACalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
value = this.curveMCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
return this.curveBCalculator.Calculate(value);
value = this.curveACalculator!.Calculate(value);
value = this.clutCalculator!.Calculate(value);
value = this.curveMCalculator!.Calculate(value);
value = this.matrixCalculator!.Calculate(value);
return this.curveBCalculator!.Calculate(value);

case CalculationType.Full | CalculationType.BtoA:
value = this.curveBCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
value = this.curveMCalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
return this.curveACalculator.Calculate(value);
value = this.curveBCalculator!.Calculate(value);
value = this.matrixCalculator!.Calculate(value);
value = this.curveMCalculator!.Calculate(value);
value = this.clutCalculator!.Calculate(value);
return this.curveACalculator!.Calculate(value);

case CalculationType.CurveClut | CalculationType.AtoB:
value = this.curveACalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
return this.curveBCalculator.Calculate(value);
value = this.curveACalculator!.Calculate(value);
value = this.clutCalculator!.Calculate(value);
return this.curveBCalculator!.Calculate(value);

case CalculationType.CurveClut | CalculationType.BtoA:
value = this.curveBCalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
return this.curveACalculator.Calculate(value);
value = this.curveBCalculator!.Calculate(value);
value = this.clutCalculator!.Calculate(value);
return this.curveACalculator!.Calculate(value);

case CalculationType.CurveMatrix | CalculationType.AtoB:
value = this.curveMCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
return this.curveBCalculator.Calculate(value);
value = this.curveMCalculator!.Calculate(value);
value = this.matrixCalculator!.Calculate(value);
return this.curveBCalculator!.Calculate(value);

case CalculationType.CurveMatrix | CalculationType.BtoA:
value = this.curveBCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
return this.curveMCalculator.Calculate(value);
value = this.curveBCalculator!.Calculate(value);
value = this.matrixCalculator!.Calculate(value);
return this.curveMCalculator!.Calculate(value);

case CalculationType.SingleCurve | CalculationType.AtoB:
case CalculationType.SingleCurve | CalculationType.BtoA:
return this.curveBCalculator.Calculate(value);
return this.curveBCalculator!.Calculate(value);

default:
throw new InvalidOperationException("Invalid calculation type");
}
}

private void Init(IccTagDataEntry[] curveA, IccTagDataEntry[] curveB, IccTagDataEntry[] curveM, Vector3? matrix3x1, Matrix4x4? matrix3x3, IccClut clut)
private void Init(IccTagDataEntry[]? curveA, IccTagDataEntry[]? curveB, IccTagDataEntry[]? curveM, Vector3? matrix3x1, Matrix4x4? matrix3x3, IccClut? clut)
{
bool hasACurve = curveA != null;
bool hasBCurve = curveB != null;
Expand Down Expand Up @@ -109,27 +108,27 @@ private void Init(IccTagDataEntry[] curveA, IccTagDataEntry[] curveB, IccTagData

if (hasACurve)
{
this.curveACalculator = new TrcCalculator(curveA, false);
this.curveACalculator = new TrcCalculator(curveA!, false);
}

if (hasBCurve)
{
this.curveBCalculator = new TrcCalculator(curveB, false);
this.curveBCalculator = new TrcCalculator(curveB!, false);
}

if (hasMCurve)
{
this.curveMCalculator = new TrcCalculator(curveM, false);
this.curveMCalculator = new TrcCalculator(curveM!, false);
}

if (hasMatrix)
{
this.matrixCalculator = new MatrixCalculator(matrix3x3.Value, matrix3x1.Value);
this.matrixCalculator = new MatrixCalculator(matrix3x3!.Value, matrix3x1!.Value);
}

if (hasClut)
{
this.clutCalculator = new ClutCalculator(clut);
this.clutCalculator = new ClutCalculator(clut!);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
Expand Down Expand Up @@ -57,6 +57,7 @@ private static Vector4 CalculateLut(LutCalculator[] lut, Vector4 value)
return value;
}

[MemberNotNull(nameof(this.inputCurve), nameof(this.outputCurve), nameof(this.clutCalculator), nameof(this.matrix))]
private void Init(IccLut[] inputCurve, IccLut[] outputCurve, IccClut clut, Matrix4x4 matrix)
{
this.inputCurve = InitLut(inputCurve);
Expand Down
13 changes: 10 additions & 3 deletions src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;

namespace SixLabors.ImageSharp.ColorProfiles.Conversion.Icc;
Expand Down Expand Up @@ -146,10 +146,17 @@ private static ConversionMethod CheckMethod2(IccProfile profile)
private static bool HasTag(IccProfile profile, IccProfileTag tag)
=> profile.Entries.Any(t => t.TagSignature == tag);

private static IccTagDataEntry GetTag(IccProfile profile, IccProfileTag tag)
private static bool TryGetTag(IccProfile profile, IccProfileTag tag, [NotNullWhen(true)] out IccTagDataEntry? entry)
{
entry = GetTag(profile, tag);

return entry is not null;
}

private static IccTagDataEntry? GetTag(IccProfile profile, IccProfileTag tag)
=> Array.Find(profile.Entries, t => t.TagSignature == tag);

private static T GetTag<T>(IccProfile profile, IccProfileTag tag)
private static T? GetTag<T>(IccProfile profile, IccProfileTag tag)
where T : IccTagDataEntry
=> profile.Entries.OfType<T>().FirstOrDefault(t => t.TagSignature == tag);
}
22 changes: 14 additions & 8 deletions src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;

Expand All @@ -24,6 +25,7 @@ internal abstract partial class IccConverterBase
/// <param name="toPcs">True if the conversion is to the Profile Connection Space.</param>
/// <param name="renderingIntent">The wanted rendering intent. Can be ignored if not available.</param>
/// <exception cref="InvalidIccProfileException">Invalid conversion method.</exception>
[MemberNotNull(nameof(this.calculator))]
protected void Init(IccProfile profile, bool toPcs, IccRenderingIntent renderingIntent)
=> this.calculator = GetConversionMethod(profile, renderingIntent) switch
{
Expand Down Expand Up @@ -73,13 +75,13 @@ private static IVector4Calculator InitD(IccProfile profile, IccProfileTag tag)

private static ColorTrcCalculator InitColorTrc(IccProfile profile, bool toPcs)
{
IccXyzTagDataEntry redMatrixColumn = GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.RedMatrixColumn);
IccXyzTagDataEntry greenMatrixColumn = GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.GreenMatrixColumn);
IccXyzTagDataEntry blueMatrixColumn = GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.BlueMatrixColumn);
IccXyzTagDataEntry? redMatrixColumn = GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.RedMatrixColumn);
IccXyzTagDataEntry? greenMatrixColumn = GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.GreenMatrixColumn);
IccXyzTagDataEntry? blueMatrixColumn = GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.BlueMatrixColumn);

IccTagDataEntry redTrc = GetTag(profile, IccProfileTag.RedTrc);
IccTagDataEntry greenTrc = GetTag(profile, IccProfileTag.GreenTrc);
IccTagDataEntry blueTrc = GetTag(profile, IccProfileTag.BlueTrc);
IccTagDataEntry? redTrc = GetTag<IccTagDataEntry>(profile, IccProfileTag.RedTrc);
IccTagDataEntry? greenTrc = GetTag<IccTagDataEntry>(profile, IccProfileTag.GreenTrc);
IccTagDataEntry? blueTrc = GetTag<IccTagDataEntry>(profile, IccProfileTag.BlueTrc);

if (redMatrixColumn == null ||
greenMatrixColumn == null ||
Expand All @@ -103,7 +105,11 @@ private static ColorTrcCalculator InitColorTrc(IccProfile profile, bool toPcs)

private static GrayTrcCalculator InitGrayTrc(IccProfile profile, bool toPcs)
{
IccTagDataEntry entry = GetTag(profile, IccProfileTag.GrayTrc);
return new GrayTrcCalculator(entry, toPcs);
if (TryGetTag(profile, IccProfileTag.GrayTrc, out IccTagDataEntry? entry))
{
return new GrayTrcCalculator(entry, toPcs);
}

throw new InvalidIccProfileException("Missing GrayTRC entry.");
}
}
3 changes: 1 addition & 2 deletions src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Numerics;
using System.Runtime.CompilerServices;
Expand All @@ -18,7 +17,7 @@ internal abstract partial class IccConverterBase
/// </summary>
/// <param name="profile">The ICC profile to use for the conversions</param>
/// <param name="toPcs">True if the conversion is to the profile connection space (PCS); False if the conversion is to the data space</param>
protected IccConverterBase(IccProfile profile, bool toPcs)
protected IccConverterBase(IccProfile? profile, bool toPcs)
{
Guard.NotNull(profile, nameof(profile));
this.Init(profile, toPcs, profile.Header.RenderingIntent);
Expand Down
21 changes: 11 additions & 10 deletions src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Metadata;
Expand Down Expand Up @@ -31,12 +30,14 @@ public static ImageMetadata Create(List<ImageFrameMetadata> frames, bool ignoreM
// ICC profile data has already been resolved in the frame metadata,
// as it is required for color conversion.
ImageFrameMetadata frameMetaData = frames[i];
if (TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes))
DebugGuard.NotNull(frameMetaData.ExifProfile);

if (TryGetIptc(frameMetaData.ExifProfile.Values, out byte[]? iptcBytes))
{
frameMetaData.IptcProfile = new IptcProfile(iptcBytes);
}

if (frameMetaData.ExifProfile.TryGetValue(ExifTag.XMP, out IExifValue<byte[]> xmpProfileBytes))
if (frameMetaData.ExifProfile.TryGetValue(ExifTag.XMP, out IExifValue<byte[]>? xmpProfileBytes))
{
frameMetaData.XmpProfile = new XmpProfile(xmpProfileBytes.Value);
}
Expand Down Expand Up @@ -65,7 +66,7 @@ private static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ImageFr
return imageMetaData;
}

private static void SetResolution(ImageMetadata imageMetaData, ExifProfile exifProfile)
private static void SetResolution(ImageMetadata imageMetaData, ExifProfile? exifProfile)
{
imageMetaData.ResolutionUnits = exifProfile != null ? UnitConverter.ExifProfileToResolutionUnit(exifProfile) : PixelResolutionUnit.PixelsPerInch;

Expand All @@ -74,34 +75,34 @@ private static void SetResolution(ImageMetadata imageMetaData, ExifProfile exifP
return;
}

if (exifProfile.TryGetValue(ExifTag.XResolution, out IExifValue<Rational> horizontalResolution))
if (exifProfile.TryGetValue(ExifTag.XResolution, out IExifValue<Rational>? horizontalResolution))
{
imageMetaData.HorizontalResolution = horizontalResolution.Value.ToDouble();
}

if (exifProfile.TryGetValue(ExifTag.YResolution, out IExifValue<Rational> verticalResolution))
if (exifProfile.TryGetValue(ExifTag.YResolution, out IExifValue<Rational>? verticalResolution))
{
imageMetaData.VerticalResolution = verticalResolution.Value.ToDouble();
}
}

private static bool TryGetIptc(IReadOnlyList<IExifValue> exifValues, out byte[] iptcBytes)
private static bool TryGetIptc(IReadOnlyList<IExifValue> exifValues, out byte[]? iptcBytes)
{
iptcBytes = null;
IExifValue iptc = exifValues.FirstOrDefault(f => f.Tag == ExifTag.IPTC);
IExifValue? iptc = exifValues.FirstOrDefault(f => f.Tag == ExifTag.IPTC);

if (iptc != null)
{
if (iptc.DataType is ExifDataType.Byte or ExifDataType.Undefined)
{
iptcBytes = (byte[])iptc.GetValue();
iptcBytes = (byte[]?)iptc.GetValue();
return true;
}

// Some Encoders write the data type of IPTC as long.
if (iptc.DataType == ExifDataType.Long)
{
uint[] iptcValues = (uint[])iptc.GetValue();
uint[] iptcValues = (uint[])iptc.GetValue()!;
iptcBytes = new byte[iptcValues.Length * 4];
Buffer.BlockCopy(iptcValues, 0, iptcBytes, 0, iptcValues.Length * 4);
if (iptcBytes[0] == 0x1c)
Expand Down
Loading
Loading