From 30a2dfaaa920f41baf422b4037f7123c86033912 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 21 Nov 2025 23:43:42 +0100 Subject: [PATCH 1/3] Remove Nullable Disable from ICC Code Removed the nullable disable from the icc Code (And one Tiff). Most parts could be solved relative easily. There are also some parts were the bang operator had to be used. --- .../Icc/Calculators/CurveCalculator.cs | 5 +- .../Icc/Calculators/GrayTrcCalculator.cs | 4 +- .../LutABCalculator.CalculationType.cs | 1 + .../Icc/Calculators/LutABCalculator.cs | 73 ++++++----- .../Icc/Calculators/LutEntryCalculator.cs | 3 +- .../Icc/Calculators/TrcCalculator.cs | 2 +- .../Icc/IccConverterBase.Checks.cs | 5 +- .../Icc/IccConverterbase.Conversions.cs | 16 +-- .../ColorProfiles/Icc/IccConverterbase.cs | 3 +- .../Tiff/TiffDecoderMetadataCreator.cs | 21 ++-- .../DataReader/IccDataReader.NonPrimitives.cs | 2 +- .../DataReader/IccDataReader.TagDataEntry.cs | 31 +++-- .../DataWriter/IccDataWriter.Primitives.cs | 2 +- .../DataWriter/IccDataWriter.TagDataEntry.cs | 3 +- .../Metadata/Profiles/ICC/IccProfile.cs | 14 ++- .../Metadata/Profiles/ICC/IccProfileHeader.cs | 7 +- .../TagDataEntries/IccLutAToBTagDataEntry.cs | 73 ++++++----- .../TagDataEntries/IccLutBToATagDataEntry.cs | 115 ++++++++++-------- .../IccTextDescriptionTagDataEntry.cs | 23 ++-- 19 files changed, 208 insertions(+), 195 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs index c39eaf958f..476b8188fe 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -9,7 +8,7 @@ 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; @@ -41,7 +40,7 @@ public float Calculate(float value) { 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), + CalculationType.Lut => this.lutCalculator!.Calculate(value), _ => throw new InvalidOperationException("Invalid calculation type"), }; } diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs index 8d823c1e95..dffbd80556 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs @@ -11,8 +11,8 @@ internal class GrayTrcCalculator : IVector4Calculator { private readonly TrcCalculator calculator; - public GrayTrcCalculator(IccTagDataEntry grayTrc, bool toPcs) - => this.calculator = new TrcCalculator(new IccTagDataEntry[] { grayTrc }, !toPcs); + public GrayTrcCalculator(IccTagDataEntry? grayTrc, bool toPcs) + => this.calculator = new TrcCalculator([grayTrc], !toPcs); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 Calculate(Vector4 value) => this.calculator.Calculate(value); diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.CalculationType.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.CalculationType.cs index 253239cb79..2e6338de65 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.CalculationType.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.CalculationType.cs @@ -5,6 +5,7 @@ namespace SixLabors.ImageSharp.ColorProfiles.Conversion.Icc; internal partial class LutABCalculator { + [Flags] private enum CalculationType { AtoB = 1 << 3, diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.cs index 172d806394..7ac21309da 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/LutABCalculator.cs @@ -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; @@ -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; } @@ -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; @@ -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!); } } } diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/LutEntryCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/LutEntryCalculator.cs index c97578ee3f..5cd934d2d6 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/LutEntryCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/LutEntryCalculator.cs @@ -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; @@ -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); diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs index d2fc5d9b55..3eb8d2e003 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs @@ -12,7 +12,7 @@ internal class TrcCalculator : IVector4Calculator { private readonly ISingleCalculator[] calculators; - public TrcCalculator(IccTagDataEntry[] entries, bool inverted) + public TrcCalculator(IccTagDataEntry?[] entries, bool inverted) { Guard.NotNull(entries, nameof(entries)); diff --git a/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs b/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs index 94f906709a..7495f9acee 100644 --- a/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs +++ b/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -146,10 +145,10 @@ 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 IccTagDataEntry? GetTag(IccProfile profile, IccProfileTag tag) => Array.Find(profile.Entries, t => t.TagSignature == tag); - private static T GetTag(IccProfile profile, IccProfileTag tag) + private static T? GetTag(IccProfile profile, IccProfileTag tag) where T : IccTagDataEntry => profile.Entries.OfType().FirstOrDefault(t => t.TagSignature == tag); } diff --git a/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs b/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs index 20df08e378..a25b15152c 100644 --- a/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs +++ b/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs @@ -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; @@ -24,6 +25,7 @@ internal abstract partial class IccConverterBase /// True if the conversion is to the Profile Connection Space. /// The wanted rendering intent. Can be ignored if not available. /// Invalid conversion method. + [MemberNotNull(nameof(this.calculator))] protected void Init(IccProfile profile, bool toPcs, IccRenderingIntent renderingIntent) => this.calculator = GetConversionMethod(profile, renderingIntent) switch { @@ -73,13 +75,13 @@ private static IVector4Calculator InitD(IccProfile profile, IccProfileTag tag) private static ColorTrcCalculator InitColorTrc(IccProfile profile, bool toPcs) { - IccXyzTagDataEntry redMatrixColumn = GetTag(profile, IccProfileTag.RedMatrixColumn); - IccXyzTagDataEntry greenMatrixColumn = GetTag(profile, IccProfileTag.GreenMatrixColumn); - IccXyzTagDataEntry blueMatrixColumn = GetTag(profile, IccProfileTag.BlueMatrixColumn); + IccXyzTagDataEntry? redMatrixColumn = GetTag(profile, IccProfileTag.RedMatrixColumn); + IccXyzTagDataEntry? greenMatrixColumn = GetTag(profile, IccProfileTag.GreenMatrixColumn); + IccXyzTagDataEntry? blueMatrixColumn = GetTag(profile, IccProfileTag.BlueMatrixColumn); - IccTagDataEntry redTrc = GetTag(profile, IccProfileTag.RedTrc); - IccTagDataEntry greenTrc = GetTag(profile, IccProfileTag.GreenTrc); - IccTagDataEntry blueTrc = GetTag(profile, IccProfileTag.BlueTrc); + IccTagDataEntry? redTrc = GetTag(profile, IccProfileTag.RedTrc); + IccTagDataEntry? greenTrc = GetTag(profile, IccProfileTag.GreenTrc); + IccTagDataEntry? blueTrc = GetTag(profile, IccProfileTag.BlueTrc); if (redMatrixColumn == null || greenMatrixColumn == null || @@ -103,7 +105,7 @@ private static ColorTrcCalculator InitColorTrc(IccProfile profile, bool toPcs) private static GrayTrcCalculator InitGrayTrc(IccProfile profile, bool toPcs) { - IccTagDataEntry entry = GetTag(profile, IccProfileTag.GrayTrc); + IccTagDataEntry? entry = GetTag(profile, IccProfileTag.GrayTrc); return new GrayTrcCalculator(entry, toPcs); } } diff --git a/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs b/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs index d9976dc2ac..70562a3092 100644 --- a/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs +++ b/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -18,7 +17,7 @@ internal abstract partial class IccConverterBase /// /// The ICC profile to use for the conversions /// True if the conversion is to the profile connection space (PCS); False if the conversion is to the data space - protected IccConverterBase(IccProfile profile, bool toPcs) + protected IccConverterBase(IccProfile? profile, bool toPcs) { Guard.NotNull(profile, nameof(profile)); this.Init(profile, toPcs, profile.Header.RenderingIntent); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index ebf407f9b5..51e984c646 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -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; @@ -31,12 +30,14 @@ public static ImageMetadata Create(List 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 xmpProfileBytes)) + if (frameMetaData.ExifProfile.TryGetValue(ExifTag.XMP, out IExifValue? xmpProfileBytes)) { frameMetaData.XmpProfile = new XmpProfile(xmpProfileBytes.Value); } @@ -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; @@ -74,34 +75,34 @@ private static void SetResolution(ImageMetadata imageMetaData, ExifProfile exifP return; } - if (exifProfile.TryGetValue(ExifTag.XResolution, out IExifValue horizontalResolution)) + if (exifProfile.TryGetValue(ExifTag.XResolution, out IExifValue? horizontalResolution)) { imageMetaData.HorizontalResolution = horizontalResolution.Value.ToDouble(); } - if (exifProfile.TryGetValue(ExifTag.YResolution, out IExifValue verticalResolution)) + if (exifProfile.TryGetValue(ExifTag.YResolution, out IExifValue? verticalResolution)) { imageMetaData.VerticalResolution = verticalResolution.Value.ToDouble(); } } - private static bool TryGetIptc(IReadOnlyList exifValues, out byte[] iptcBytes) + private static bool TryGetIptc(IReadOnlyList 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) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs index 219e785590..ffe47df7cc 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs @@ -144,7 +144,7 @@ IccMultiLocalizedUnicodeTagDataEntry ReadText() case IccTypeSignature.MultiLocalizedUnicode: return this.ReadMultiLocalizedUnicodeTagDataEntry(); case IccTypeSignature.TextDescription: - return (IccMultiLocalizedUnicodeTagDataEntry)this.ReadTextDescriptionTagDataEntry(); + return ((IccMultiLocalizedUnicodeTagDataEntry)this.ReadTextDescriptionTagDataEntry())!; default: throw new InvalidIccProfileException("Profile description can only have multi-localized Unicode or text description entries"); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index 9e89d24ff4..5c6f1e85af 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Numerics; @@ -332,12 +331,12 @@ public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry() 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; + IccTagDataEntry[]? bCurve = null; + IccTagDataEntry[]? mCurve = null; + IccTagDataEntry[]? aCurve = null; + IccClut? clut = null; + float[,]? matrix3x3 = null; + float[]? matrix3x1 = null; if (bCurveOffset != 0) { @@ -391,12 +390,12 @@ public IccLutBToATagDataEntry ReadLutBtoATagDataEntry() 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; + IccTagDataEntry[]? bCurve = null; + IccTagDataEntry[]? mCurve = null; + IccTagDataEntry[]? aCurve = null; + IccClut? clut = null; + float[,]? matrix3x3 = null; + float[]? matrix3x1 = null; if (bCurveOffset != 0) { @@ -477,7 +476,7 @@ public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntr return new IccMultiLocalizedUnicodeTagDataEntry(text); - CultureInfo ReadCulture(string language, string country) + CultureInfo ReadCulture(string language, string? country) { if (string.IsNullOrWhiteSpace(language)) { @@ -775,8 +774,8 @@ public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) /// The read entry. public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() { - string unicodeValue, scriptcodeValue; - string asciiValue = unicodeValue = scriptcodeValue = null; + string? unicodeValue, scriptcodeValue; + string? asciiValue = unicodeValue = scriptcodeValue = null; int asciiCount = (int)this.ReadUInt32(); if (asciiCount > 0) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs index e1eebb749b..a668c61207 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs @@ -189,7 +189,7 @@ public int WriteAsciiString(string value) /// The desired length of the string (including potential null terminator) /// If True, there will be a \0 added at the end /// the number of bytes written - public int WriteAsciiString(string value, int length, bool ensureNullTerminator) + public int WriteAsciiString(string? value, int length, bool ensureNullTerminator) { if (length == 0) { diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index 6019a0bff7..15e53a2b46 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -71,7 +70,7 @@ public int WriteTagDataEntry(IccTagDataEntry entry) IccTypeSignature.UcrBg => this.WriteUcrBgTagDataEntry((IccUcrBgTagDataEntry)entry), // Unsupported or unknown - _ => this.WriteUnknownTagDataEntry(entry as IccUnknownTagDataEntry), + _ => this.WriteUnknownTagDataEntry((entry as IccUnknownTagDataEntry)!), }; return count; } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs index 05be3eb5dd..b93d5e9fa6 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -14,23 +14,23 @@ public sealed partial class IccProfile : IDeepCloneable /// /// The byte array to read the ICC profile from /// - private readonly byte[] data; + private readonly byte[]? data; /// /// The backing file for the property /// - private IccTagDataEntry[] entries; + private IccTagDataEntry[]? entries; /// /// ICC profile header /// - private IccProfileHeader header; + private IccProfileHeader? header; /// /// Initializes a new instance of the class. /// public IccProfile() - : this((byte[])null) + : this((byte[]?)null) { } @@ -38,7 +38,7 @@ public IccProfile() /// Initializes a new instance of the class. /// /// The raw ICC profile data - public IccProfile(byte[] data) => this.data = data; + public IccProfile(byte[]? data) => this.data = data; /// /// Initializes a new instance of the class. @@ -177,6 +177,7 @@ public byte[] ToByteArray() return IccWriter.Write(this); } + [MemberNotNull(nameof(this.header))] private void InitializeHeader() { if (this.header != null) @@ -193,6 +194,7 @@ private void InitializeHeader() this.header = IccReader.ReadHeader(this.data); } + [MemberNotNull(nameof(this.entries))] private void InitializeEntries() { if (this.entries != null) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs index 959668aaf9..cea2a6cc23 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; @@ -19,7 +18,7 @@ public sealed class IccProfileHeader /// /// Gets or sets the preferred CMM (Color Management Module) type. /// - public string CmmType { get; set; } + public string? CmmType { get; set; } /// /// Gets or sets the profiles version number. @@ -50,7 +49,7 @@ public sealed class IccProfileHeader /// Gets or sets the file signature. Should always be "acsp". /// Value will be ignored when writing a profile. /// - public string FileSignature { get; set; } + public string? FileSignature { get; set; } /// /// Gets or sets the primary platform this profile as created for @@ -91,7 +90,7 @@ public sealed class IccProfileHeader /// /// Gets or sets profile creator signature. /// - public string CreatorSignature { get; set; } + public string? CreatorSignature { get; set; } /// /// Gets or sets the profile ID (hash). diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs index 9bf3232633..9d404ddb1d 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable +using System.Diagnostics.CodeAnalysis; using System.Numerics; // TODO: Review the use of base IccTagDataEntry comparison. @@ -22,12 +22,12 @@ internal sealed class IccLutAToBTagDataEntry : IccTagDataEntry, IEquatableCLUT /// A Curve public IccLutAToBTagDataEntry( - IccTagDataEntry[] curveB, - float[,] matrix3x3, - float[] matrix3x1, - IccTagDataEntry[] curveM, - IccClut clutValues, - IccTagDataEntry[] curveA) + IccTagDataEntry[]? curveB, + float[,]? matrix3x3, + float[]? matrix3x1, + IccTagDataEntry[]? curveM, + IccClut? clutValues, + IccTagDataEntry[]? curveA) : this(curveB, matrix3x3, matrix3x1, curveM, clutValues, curveA, IccProfileTag.Unknown) { } @@ -43,12 +43,12 @@ public IccLutAToBTagDataEntry( /// A Curve /// Tag Signature public IccLutAToBTagDataEntry( - IccTagDataEntry[] curveB, - float[,] matrix3x3, - float[] matrix3x1, - IccTagDataEntry[] curveM, - IccClut clutValues, - IccTagDataEntry[] curveA, + IccTagDataEntry[]? curveB, + float[,]? matrix3x3, + float[]? matrix3x1, + IccTagDataEntry[]? curveM, + IccClut? clutValues, + IccTagDataEntry[]? curveA, IccProfileTag tagSignature) : base(IccTypeSignature.LutAToB, tagSignature) { @@ -70,11 +70,11 @@ public IccLutAToBTagDataEntry( Guard.IsTrue(this.CurveM.Length == 3, nameof(this.CurveM), $"{nameof(this.CurveM)} must have a length of three"); Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); - this.InputChannelCount = curveA.Length; + this.InputChannelCount = this.CurveA.Length; this.OutputChannelCount = 3; - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); + Guard.IsTrue(this.InputChannelCount == this.ClutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); + Guard.IsTrue(this.OutputChannelCount == this.ClutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); } else if (this.IsMMatrixB()) { @@ -88,11 +88,11 @@ public IccLutAToBTagDataEntry( Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); Guard.MustBeBetweenOrEqualTo(this.CurveB.Length, 1, 15, nameof(this.CurveB)); - this.InputChannelCount = curveA.Length; - this.OutputChannelCount = curveB.Length; + this.InputChannelCount = this.CurveA.Length; + this.OutputChannelCount = this.CurveB.Length; - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); + Guard.IsTrue(this.InputChannelCount == this.ClutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); + Guard.IsTrue(this.OutputChannelCount == this.ClutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); } else if (this.IsB()) { @@ -127,28 +127,28 @@ public IccLutAToBTagDataEntry( /// /// Gets the color lookup table /// - public IccClut ClutValues { get; } + public IccClut? ClutValues { get; } /// /// Gets the B Curve /// - public IccTagDataEntry[] CurveB { get; } + public IccTagDataEntry[]? CurveB { get; } /// /// Gets the M Curve /// - public IccTagDataEntry[] CurveM { get; } + public IccTagDataEntry[]? CurveM { get; } /// /// Gets the A Curve /// - public IccTagDataEntry[] CurveA { get; } + public IccTagDataEntry[]? CurveA { get; } /// - public override bool Equals(IccTagDataEntry other) => other is IccLutAToBTagDataEntry entry && this.Equals(entry); + public override bool Equals(IccTagDataEntry? other) => other is IccLutAToBTagDataEntry entry && this.Equals(entry); /// - public bool Equals(IccLutAToBTagDataEntry other) + public bool Equals(IccLutAToBTagDataEntry? other) { if (other is null) { @@ -165,14 +165,14 @@ public bool Equals(IccLutAToBTagDataEntry other) && this.OutputChannelCount == other.OutputChannelCount && this.Matrix3x3.Equals(other.Matrix3x3) && this.Matrix3x1.Equals(other.Matrix3x1) - && this.ClutValues.Equals(other.ClutValues) + && this.ClutValues!.Equals(other.ClutValues) && EqualsCurve(this.CurveB, other.CurveB) && EqualsCurve(this.CurveM, other.CurveM) && EqualsCurve(this.CurveA, other.CurveA); } /// - public override bool Equals(object obj) => obj is IccLutAToBTagDataEntry other && this.Equals(other); + public override bool Equals(object? obj) => obj is IccLutAToBTagDataEntry other && this.Equals(other); /// public override int GetHashCode() @@ -192,7 +192,7 @@ public override int GetHashCode() return hashCode.ToHashCode(); } - private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) + private static bool EqualsCurve(IccTagDataEntry[]? thisCurves, IccTagDataEntry[]? entryCurves) { bool thisNull = thisCurves is null; bool entryNull = entryCurves is null; @@ -207,9 +207,13 @@ private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] return false; } + DebugGuard.NotNull(thisCurves); + DebugGuard.NotNull(entryCurves); + return thisCurves.SequenceEqual(entryCurves); } + [MemberNotNullWhen(true, nameof(this.CurveB), nameof(Matrix3x3), nameof(this.Matrix3x1), nameof(this.CurveM), nameof(this.ClutValues), nameof(this.CurveA))] private bool IsAClutMMatrixB() => this.CurveB != null && this.Matrix3x3 != null @@ -218,20 +222,23 @@ private bool IsAClutMMatrixB() && this.ClutValues != null && this.CurveA != null; + [MemberNotNullWhen(true, nameof(this.CurveB), nameof(Matrix3x3), nameof(this.Matrix3x1), nameof(this.CurveM))] private bool IsMMatrixB() => this.CurveB != null && this.Matrix3x3 != null && this.Matrix3x1 != null && this.CurveM != null; + [MemberNotNullWhen(true, nameof(this.CurveB), nameof(this.ClutValues), nameof(this.CurveA))] private bool IsAClutB() => this.CurveB != null && this.ClutValues != null && this.CurveA != null; + [MemberNotNullWhen(true, nameof(this.CurveB))] private bool IsB() => this.CurveB != null; - private void VerifyCurve(IccTagDataEntry[] curves, string name) + private void VerifyCurve(IccTagDataEntry[]? curves, string name) { if (curves != null) { @@ -240,7 +247,7 @@ private void VerifyCurve(IccTagDataEntry[] curves, string name) } } - private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) + private static void VerifyMatrix(float[,]? matrix3x3, float[]? matrix3x1) { if (matrix3x1 != null) { @@ -254,7 +261,7 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) } } - private static Vector3? CreateMatrix3x1(float[] matrix) + private static Vector3? CreateMatrix3x1(float[]? matrix) { if (matrix is null) { @@ -264,7 +271,7 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) return new Vector3(matrix[0], matrix[1], matrix[2]); } - private static Matrix4x4? CreateMatrix3x3(float[,] matrix) + private static Matrix4x4? CreateMatrix3x3(float[,]? matrix) { if (matrix is null) { diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs index 033b809894..73b884c67f 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable +using System.Diagnostics.CodeAnalysis; using System.Numerics; // TODO: Review the use of base IccTagDataEntry comparison. @@ -15,19 +15,19 @@ internal sealed class IccLutBToATagDataEntry : IccTagDataEntry, IEquatable /// Initializes a new instance of the class. /// - /// B Curve - /// Two dimensional conversion matrix (3x3) - /// One dimensional conversion matrix (3x1) - /// M Curve - /// CLUT - /// A Curve + /// B Curve. + /// Two dimensional conversion matrix (3x3). + /// One dimensional conversion matrix (3x1). + /// M Curve. + /// CLUT. + /// A Curve. public IccLutBToATagDataEntry( - IccTagDataEntry[] curveB, - float[,] matrix3x3, - float[] matrix3x1, - IccTagDataEntry[] curveM, - IccClut clutValues, - IccTagDataEntry[] curveA) + IccTagDataEntry[]? curveB, + float[,]? matrix3x3, + float[]? matrix3x1, + IccTagDataEntry[]? curveM, + IccClut? clutValues, + IccTagDataEntry[]? curveA) : this(curveB, matrix3x3, matrix3x1, curveM, clutValues, curveA, IccProfileTag.Unknown) { } @@ -35,20 +35,20 @@ public IccLutBToATagDataEntry( /// /// Initializes a new instance of the class. /// - /// B Curve - /// Two dimensional conversion matrix (3x3) - /// One dimensional conversion matrix (3x1) - /// M Curve - /// CLUT - /// A Curve - /// Tag Signature + /// B Curve. + /// Two dimensional conversion matrix (3x3). + /// One dimensional conversion matrix (3x1). + /// M Curve. + /// CLUT. + /// A Curve. + /// Tag Signature. public IccLutBToATagDataEntry( - IccTagDataEntry[] curveB, - float[,] matrix3x3, - float[] matrix3x1, - IccTagDataEntry[] curveM, - IccClut clutValues, - IccTagDataEntry[] curveA, + IccTagDataEntry[]? curveB, + float[,]? matrix3x3, + float[]? matrix3x1, + IccTagDataEntry[]? curveM, + IccClut? clutValues, + IccTagDataEntry[]? curveA, IccProfileTag tagSignature) : base(IccTypeSignature.LutBToA, tagSignature) { @@ -71,10 +71,10 @@ public IccLutBToATagDataEntry( Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); this.InputChannelCount = 3; - this.OutputChannelCount = curveA.Length; + this.OutputChannelCount = this.CurveA.Length; - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); + Guard.IsTrue(this.InputChannelCount == this.ClutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); + Guard.IsTrue(this.OutputChannelCount == this.ClutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); } else if (this.IsBMatrixM()) { @@ -88,11 +88,11 @@ public IccLutBToATagDataEntry( Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); Guard.MustBeBetweenOrEqualTo(this.CurveB.Length, 1, 15, nameof(this.CurveB)); - this.InputChannelCount = curveB.Length; - this.OutputChannelCount = curveA.Length; + this.InputChannelCount = this.CurveB.Length; + this.OutputChannelCount = this.CurveA.Length; - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); + Guard.IsTrue(this.InputChannelCount == this.ClutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); + Guard.IsTrue(this.OutputChannelCount == this.ClutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); } else if (this.IsB()) { @@ -105,50 +105,50 @@ public IccLutBToATagDataEntry( } /// - /// Gets the number of input channels + /// Gets the number of input channels. /// public int InputChannelCount { get; } /// - /// Gets the number of output channels + /// Gets the number of output channels. /// public int OutputChannelCount { get; } /// - /// Gets the two dimensional conversion matrix (3x3) + /// Gets the two dimensional conversion matrix (3x3). /// public Matrix4x4? Matrix3x3 { get; } /// - /// Gets the one dimensional conversion matrix (3x1) + /// Gets the one dimensional conversion matrix (3x1). /// public Vector3? Matrix3x1 { get; } /// - /// Gets the color lookup table + /// Gets the color lookup table. /// - public IccClut ClutValues { get; } + public IccClut? ClutValues { get; } /// - /// Gets the B Curve + /// Gets the B Curve. /// - public IccTagDataEntry[] CurveB { get; } + public IccTagDataEntry[]? CurveB { get; } /// - /// Gets the M Curve + /// Gets the M Curve. /// - public IccTagDataEntry[] CurveM { get; } + public IccTagDataEntry[]? CurveM { get; } /// - /// Gets the A Curve + /// Gets the A Curve. /// - public IccTagDataEntry[] CurveA { get; } + public IccTagDataEntry[]? CurveA { get; } /// - public override bool Equals(IccTagDataEntry other) => other is IccLutBToATagDataEntry entry && this.Equals(entry); + public override bool Equals(IccTagDataEntry? other) => other is IccLutBToATagDataEntry entry && this.Equals(entry); /// - public bool Equals(IccLutBToATagDataEntry other) + public bool Equals(IccLutBToATagDataEntry? other) { if (other is null) { @@ -165,14 +165,14 @@ public bool Equals(IccLutBToATagDataEntry other) && this.OutputChannelCount == other.OutputChannelCount && this.Matrix3x3.Equals(other.Matrix3x3) && this.Matrix3x1.Equals(other.Matrix3x1) - && this.ClutValues.Equals(other.ClutValues) + && this.ClutValues!.Equals(other.ClutValues) && EqualsCurve(this.CurveB, other.CurveB) && EqualsCurve(this.CurveM, other.CurveM) && EqualsCurve(this.CurveA, other.CurveA); } /// - public override bool Equals(object obj) => obj is IccLutBToATagDataEntry other && this.Equals(other); + public override bool Equals(object? obj) => obj is IccLutBToATagDataEntry other && this.Equals(other); /// public override int GetHashCode() @@ -191,7 +191,7 @@ public override int GetHashCode() return hashCode.ToHashCode(); } - private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) + private static bool EqualsCurve(IccTagDataEntry[]? thisCurves, IccTagDataEntry[]? entryCurves) { bool thisNull = thisCurves is null; bool entryNull = entryCurves is null; @@ -206,21 +206,28 @@ private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] return false; } + DebugGuard.NotNull(thisCurves); + DebugGuard.NotNull(entryCurves); + return thisCurves.SequenceEqual(entryCurves); } + [MemberNotNullWhen(true, nameof(this.CurveB), nameof(Matrix3x3), nameof(Matrix3x1), nameof(CurveM), nameof(ClutValues), nameof(CurveA))] private bool IsBMatrixMClutA() => this.CurveB != null && this.Matrix3x3 != null && this.Matrix3x1 != null && this.CurveM != null && this.ClutValues != null && this.CurveA != null; + [MemberNotNullWhen(true, nameof(this.CurveB), nameof(this.Matrix3x3), nameof(this.Matrix3x1), nameof(this.CurveM))] private bool IsBMatrixM() => this.CurveB != null && this.Matrix3x3 != null && this.Matrix3x1 != null && this.CurveM != null; + [MemberNotNullWhen(true, nameof(this.CurveB), nameof(this.ClutValues), nameof(this.CurveA))] private bool IsBClutA() => this.CurveB != null && this.ClutValues != null && this.CurveA != null; + [MemberNotNullWhen(true, nameof(this.CurveB))] private bool IsB() => this.CurveB != null; - private void VerifyCurve(IccTagDataEntry[] curves, string name) + private void VerifyCurve(IccTagDataEntry[]? curves, string name) { if (curves != null) { @@ -229,7 +236,7 @@ private void VerifyCurve(IccTagDataEntry[] curves, string name) } } - private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) + private static void VerifyMatrix(float[,]? matrix3x3, float[]? matrix3x1) { if (matrix3x1 != null) { @@ -243,7 +250,7 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) } } - private static Vector3? CreateMatrix3x1(float[] matrix) + private static Vector3? CreateMatrix3x1(float[]? matrix) { if (matrix is null) { @@ -253,7 +260,7 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) return new Vector3(matrix[0], matrix[1], matrix[2]); } - private static Matrix4x4? CreateMatrix3x3(float[,] matrix) + private static Matrix4x4? CreateMatrix3x3(float[,]? matrix) { if (matrix is null) { diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs index 7db26e5c58..6d21fe93b0 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; @@ -19,7 +18,7 @@ internal sealed class IccTextDescriptionTagDataEntry : IccTagDataEntry, IEquatab /// ScriptCode text /// Unicode Language-Code /// ScriptCode Code - public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scriptCode, uint unicodeLanguageCode, ushort scriptCodeCode) + public IccTextDescriptionTagDataEntry(string? ascii, string? unicode, string? scriptCode, uint unicodeLanguageCode, ushort scriptCodeCode) : this(ascii, unicode, scriptCode, unicodeLanguageCode, scriptCodeCode, IccProfileTag.Unknown) { } @@ -33,7 +32,7 @@ public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scrip /// Unicode Language-Code /// ScriptCode Code /// Tag Signature - public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scriptCode, uint unicodeLanguageCode, ushort scriptCodeCode, IccProfileTag tagSignature) + public IccTextDescriptionTagDataEntry(string? ascii, string? unicode, string? scriptCode, uint unicodeLanguageCode, ushort scriptCodeCode, IccProfileTag tagSignature) : base(IccTypeSignature.TextDescription, tagSignature) { this.Ascii = ascii; @@ -46,17 +45,17 @@ public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scrip /// /// Gets the ASCII text /// - public string Ascii { get; } + public string? Ascii { get; } /// /// Gets the Unicode text /// - public string Unicode { get; } + public string? Unicode { get; } /// /// Gets the ScriptCode text /// - public string ScriptCode { get; } + public string? ScriptCode { get; } /// /// Gets the Unicode Language-Code @@ -74,7 +73,7 @@ public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scrip /// /// The entry to convert /// The converted entry - public static explicit operator IccMultiLocalizedUnicodeTagDataEntry(IccTextDescriptionTagDataEntry textEntry) + public static explicit operator IccMultiLocalizedUnicodeTagDataEntry?(IccTextDescriptionTagDataEntry? textEntry) { if (textEntry is null) { @@ -84,7 +83,7 @@ public static explicit operator IccMultiLocalizedUnicodeTagDataEntry(IccTextDesc IccLocalizedString localString; if (!string.IsNullOrEmpty(textEntry.Unicode)) { - CultureInfo culture = GetCulture(textEntry.UnicodeLanguageCode); + CultureInfo? culture = GetCulture(textEntry.UnicodeLanguageCode); localString = culture != null ? new IccLocalizedString(culture, textEntry.Unicode) : new IccLocalizedString(textEntry.Unicode); @@ -104,7 +103,7 @@ public static explicit operator IccMultiLocalizedUnicodeTagDataEntry(IccTextDesc return new IccMultiLocalizedUnicodeTagDataEntry(new[] { localString }, textEntry.TagSignature); - static CultureInfo GetCulture(uint value) + static CultureInfo? GetCulture(uint value) { if (value == 0) { @@ -131,11 +130,11 @@ static CultureInfo GetCulture(uint value) } /// - public override bool Equals(IccTagDataEntry other) + public override bool Equals(IccTagDataEntry? other) => other is IccTextDescriptionTagDataEntry entry && this.Equals(entry); /// - public bool Equals(IccTextDescriptionTagDataEntry other) + public bool Equals(IccTextDescriptionTagDataEntry? other) { if (other is null) { @@ -156,7 +155,7 @@ public bool Equals(IccTextDescriptionTagDataEntry other) } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is IccTextDescriptionTagDataEntry other && this.Equals(other); /// From b4cfa7e95f4a3793e70777f60e12323649fa769f Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 22 Nov 2025 08:38:19 +0100 Subject: [PATCH 2/3] Address PR Feedback Removed not needed nullability Refactored CurveCalculator.Calculate to not need the bang operator --- .../Icc/Calculators/CurveCalculator.cs | 27 ++++++++++++++----- .../Icc/Calculators/GrayTrcCalculator.cs | 2 +- .../Icc/Calculators/TrcCalculator.cs | 2 +- .../Icc/IccConverterBase.Checks.cs | 4 +-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs index 476b8188fe..1544276e64 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/CurveCalculator.cs @@ -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; @@ -35,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"); + } } diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs index dffbd80556..1016f829be 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/GrayTrcCalculator.cs @@ -11,7 +11,7 @@ internal class GrayTrcCalculator : IVector4Calculator { private readonly TrcCalculator calculator; - public GrayTrcCalculator(IccTagDataEntry? grayTrc, bool toPcs) + public GrayTrcCalculator(IccTagDataEntry grayTrc, bool toPcs) => this.calculator = new TrcCalculator([grayTrc], !toPcs); [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs b/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs index 3eb8d2e003..d2fc5d9b55 100644 --- a/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs +++ b/src/ImageSharp/ColorProfiles/Icc/Calculators/TrcCalculator.cs @@ -12,7 +12,7 @@ internal class TrcCalculator : IVector4Calculator { private readonly ISingleCalculator[] calculators; - public TrcCalculator(IccTagDataEntry?[] entries, bool inverted) + public TrcCalculator(IccTagDataEntry[] entries, bool inverted) { Guard.NotNull(entries, nameof(entries)); diff --git a/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs b/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs index 7495f9acee..3254cd676d 100644 --- a/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs +++ b/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs @@ -145,8 +145,8 @@ 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) - => Array.Find(profile.Entries, t => t.TagSignature == tag); + private static IccTagDataEntry GetTag(IccProfile profile, IccProfileTag tag) + => Array.Find(profile.Entries, t => t.TagSignature == tag) ?? throw new InvalidOperationException(); private static T? GetTag(IccProfile profile, IccProfileTag tag) where T : IccTagDataEntry From 1f79e96a7fde77f447d7a04433605303c5bf3fd1 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 22 Nov 2025 15:41:36 +0100 Subject: [PATCH 3/3] Added TryGetTag --- .../ColorProfiles/Icc/IccConverterBase.Checks.cs | 12 ++++++++++-- .../Icc/IccConverterbase.Conversions.cs | 14 +++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs b/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs index 3254cd676d..c108ac7758 100644 --- a/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs +++ b/src/ImageSharp/ColorProfiles/Icc/IccConverterBase.Checks.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Metadata.Profiles.Icc; namespace SixLabors.ImageSharp.ColorProfiles.Conversion.Icc; @@ -145,8 +146,15 @@ 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) - => Array.Find(profile.Entries, t => t.TagSignature == tag) ?? throw new InvalidOperationException(); + 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(IccProfile profile, IccProfileTag tag) where T : IccTagDataEntry diff --git a/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs b/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs index a25b15152c..4bbd961ab2 100644 --- a/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs +++ b/src/ImageSharp/ColorProfiles/Icc/IccConverterbase.Conversions.cs @@ -79,9 +79,9 @@ private static ColorTrcCalculator InitColorTrc(IccProfile profile, bool toPcs) IccXyzTagDataEntry? greenMatrixColumn = GetTag(profile, IccProfileTag.GreenMatrixColumn); IccXyzTagDataEntry? blueMatrixColumn = GetTag(profile, IccProfileTag.BlueMatrixColumn); - IccTagDataEntry? redTrc = GetTag(profile, IccProfileTag.RedTrc); - IccTagDataEntry? greenTrc = GetTag(profile, IccProfileTag.GreenTrc); - IccTagDataEntry? blueTrc = GetTag(profile, IccProfileTag.BlueTrc); + IccTagDataEntry? redTrc = GetTag(profile, IccProfileTag.RedTrc); + IccTagDataEntry? greenTrc = GetTag(profile, IccProfileTag.GreenTrc); + IccTagDataEntry? blueTrc = GetTag(profile, IccProfileTag.BlueTrc); if (redMatrixColumn == null || greenMatrixColumn == null || @@ -105,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."); } }