From 07d116d8d1bfe0c2230add108f953f5eeb6fcfe3 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Tue, 17 Jan 2023 13:27:37 +0100 Subject: [PATCH 1/7] Remove #nullable disable from ExifProfile --- .../Common/Helpers/UnitConverter.cs | 2 +- .../Profiles/Exif/ExifEncodedStringHelpers.cs | 4 +-- .../Metadata/Profiles/Exif/ExifProfile.cs | 31 ++++++++++--------- .../Metadata/Profiles/Exif/ExifReader.cs | 27 ++++++++-------- .../Exif/ExifTagDescriptionAttribute.cs | 9 +++--- .../Metadata/Profiles/Exif/ExifWriter.cs | 28 +++++++++-------- .../Exif/Values/ExifArrayValue{TValueType}.cs | 7 ++--- .../Metadata/Profiles/Exif/Values/ExifByte.cs | 2 +- .../Profiles/Exif/Values/ExifByteArray.cs | 2 +- .../Profiles/Exif/Values/ExifDouble.cs | 2 +- .../Profiles/Exif/Values/ExifEncodedString.cs | 2 +- .../Profiles/Exif/Values/ExifFloat.cs | 2 +- .../Metadata/Profiles/Exif/Values/ExifLong.cs | 2 +- .../Profiles/Exif/Values/ExifLong8.cs | 2 +- .../Profiles/Exif/Values/ExifLong8Array.cs | 2 +- .../Profiles/Exif/Values/ExifNumber.cs | 2 +- .../Profiles/Exif/Values/ExifNumberArray.cs | 2 +- .../Profiles/Exif/Values/ExifRational.cs | 2 +- .../Profiles/Exif/Values/ExifRationalArray.cs | 2 +- .../Profiles/Exif/Values/ExifShort.cs | 2 +- .../Profiles/Exif/Values/ExifShortArray.cs | 2 +- .../Profiles/Exif/Values/ExifSignedByte.cs | 2 +- .../Profiles/Exif/Values/ExifSignedShort.cs | 2 +- .../Exif/Values/ExifSignedShortArray.cs | 2 +- .../Profiles/Exif/Values/ExifString.cs | 4 +-- .../Profiles/Exif/Values/ExifUcs2String.cs | 6 ++-- .../Profiles/Exif/Values/ExifValue.cs | 11 +++---- .../Profiles/Exif/Values/ExifValues.cs | 11 +++---- .../Exif/Values/ExifValue{TValueType}.cs | 18 ++++------- .../Profiles/Exif/Values/IExifValue.cs | 4 +-- .../Exif/Values/IExifValue{TValueType}.cs | 2 +- .../Linear/AutoOrientProcessor{TPixel}.cs | 2 +- 32 files changed, 97 insertions(+), 103 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs index 04ecb5afd..983d86a71 100644 --- a/src/ImageSharp/Common/Helpers/UnitConverter.cs +++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs @@ -91,7 +91,7 @@ internal static class UnitConverter [MethodImpl(InliningOptions.ShortMethod)] public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile) { - IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit); + IExifValue? resolution = profile.GetValue(ExifTag.ResolutionUnit); // EXIF is 1, 2, 3 so we minus "1" off the result. return resolution is null ? DefaultResolutionUnit : (PixelResolutionUnit)(byte)(resolution.Value - 1); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs index 8d6723c28..bfe991f83 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; using System.Text; @@ -27,7 +26,8 @@ internal static class ExifEncodedStringHelpers // 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990) // https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-6.0 - private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(20932); + private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(20932) ?? + throw new InvalidOperationException(); public static bool IsEncodedString(ExifTagValue tag) => tag switch { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index dbe19d8b6..9a41fb6a4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -1,7 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -14,12 +15,12 @@ public sealed class ExifProfile : IDeepCloneable /// /// The byte array to read the EXIF profile from. /// - private readonly byte[] data; + private readonly byte[]? data; /// /// The collection of EXIF values /// - private List values; + private List? values; /// /// The thumbnail offset position in the byte stream @@ -35,7 +36,7 @@ public sealed class ExifProfile : IDeepCloneable /// Initializes a new instance of the class. /// public ExifProfile() - : this((byte[])null) + : this((byte[]?)null) { } @@ -43,7 +44,7 @@ public sealed class ExifProfile : IDeepCloneable /// Initializes a new instance of the class. /// /// The byte array to read the EXIF profile from. - public ExifProfile(byte[] data) + public ExifProfile(byte[]? data) { this.Parts = ExifParts.All; this.data = data; @@ -110,6 +111,7 @@ public sealed class ExifProfile : IDeepCloneable /// /// Gets the values of this EXIF profile. /// + [MemberNotNull(nameof(values))] public IReadOnlyList Values { get @@ -125,7 +127,7 @@ public sealed class ExifProfile : IDeepCloneable /// /// The . /// - public Image CreateThumbnail() => this.CreateThumbnail(); + public Image? CreateThumbnail() => this.CreateThumbnail(); /// /// Returns the thumbnail in the EXIF profile when available. @@ -134,7 +136,7 @@ public sealed class ExifProfile : IDeepCloneable /// /// The . /// - public Image CreateThumbnail() + public Image? CreateThumbnail() where TPixel : unmanaged, IPixel { this.InitializeValues(); @@ -161,9 +163,9 @@ public sealed class ExifProfile : IDeepCloneable /// The tag of the exif value. /// The value with the specified tag. /// The data type of the tag. - public IExifValue GetValue(ExifTag tag) + public IExifValue? GetValue(ExifTag tag) { - IExifValue value = this.GetValueInternal(tag); + IExifValue? value = this.GetValueInternal(tag); return value is null ? null : (IExifValue)value; } @@ -203,7 +205,7 @@ public sealed class ExifProfile : IDeepCloneable /// Converts this instance to a byte array. /// /// The - public byte[] ToByteArray() + public byte[]? ToByteArray() { if (this.values is null) { @@ -227,7 +229,7 @@ public sealed class ExifProfile : IDeepCloneable /// /// The tag of the exif value. /// The value with the specified tag. - internal IExifValue GetValueInternal(ExifTag tag) + internal IExifValue? GetValueInternal(ExifTag tag) { foreach (IExifValue exifValue in this.Values) { @@ -245,7 +247,7 @@ public sealed class ExifProfile : IDeepCloneable /// /// The tag of the exif value. /// The value. - internal void SetValueInternal(ExifTag tag, object value) + internal void SetValueInternal(ExifTag tag, object? value) { foreach (IExifValue exifValue in this.Values) { @@ -256,7 +258,7 @@ public sealed class ExifProfile : IDeepCloneable } } - ExifValue newExifValue = ExifValues.Create(tag); + ExifValue? newExifValue = ExifValues.Create(tag); if (newExifValue is null) { throw new NotSupportedException(); @@ -278,7 +280,7 @@ public sealed class ExifProfile : IDeepCloneable private void SyncResolution(ExifTag tag, double resolution) { - IExifValue value = this.GetValue(tag); + IExifValue? value = this.GetValue(tag); if (value is null) { @@ -294,6 +296,7 @@ public sealed class ExifProfile : IDeepCloneable this.SetValue(tag, newResolution); } + [MemberNotNull(nameof(values))] private void InitializeValues() { if (this.values != null) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index a93973a8d..7b989c8d8 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Buffers.Binary; @@ -20,7 +19,7 @@ internal class ExifReader : BaseExifReader { } - public ExifReader(byte[] exifData, MemoryAllocator allocator) + public ExifReader(byte[] exifData, MemoryAllocator? allocator) : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData))), allocator) { } @@ -90,13 +89,13 @@ internal abstract class BaseExifReader private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf2 = new byte[2]; - private readonly MemoryAllocator allocator; + private readonly MemoryAllocator? allocator; private readonly Stream data; - private List invalidTags; + private List? invalidTags; - private List subIfds; + private List? subIfds; - protected BaseExifReader(Stream stream, MemoryAllocator allocator) + protected BaseExifReader(Stream stream, MemoryAllocator? allocator) { this.data = stream ?? throw new ArgumentNullException(nameof(stream)); this.allocator = allocator; @@ -219,7 +218,7 @@ internal abstract class BaseExifReader this.Seek(tag.Offset); if (this.TryReadSpan(buffer)) { - object value = this.ConvertValue(tag.DataType, buffer, tag.NumberOfComponents > 1 || tag.Exif.IsArray); + object? value = this.ConvertValue(tag.DataType, buffer, tag.NumberOfComponents > 1 || tag.Exif.IsArray); this.Add(values, tag.Exif, value); } } @@ -255,7 +254,7 @@ internal abstract class BaseExifReader private static byte ConvertToByte(ReadOnlySpan buffer) => buffer[0]; - private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, bool isArray) + private object? ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, bool isArray) { if (buffer.Length == 0) { @@ -390,7 +389,7 @@ internal abstract class BaseExifReader numberOfComponents = 4 / ExifDataTypes.GetSize(dataType); } - ExifValue exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); + ExifValue? exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); if (exifValue is null) { @@ -414,7 +413,7 @@ internal abstract class BaseExifReader } else { - object value = this.ConvertValue(dataType, offsetBuffer[..(int)size], numberOfComponents > 1 || exifValue.IsArray); + object? value = this.ConvertValue(dataType, offsetBuffer[..(int)size], numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } @@ -443,7 +442,7 @@ internal abstract class BaseExifReader numberOfComponents = 8 / ExifDataTypes.GetSize(dataType); } - ExifValue exifValue = tag switch + ExifValue? exifValue = tag switch { ExifTagValue.StripOffsets => new ExifLong8Array(ExifTagValue.StripOffsets), ExifTagValue.StripByteCounts => new ExifLong8Array(ExifTagValue.StripByteCounts), @@ -471,12 +470,12 @@ internal abstract class BaseExifReader } else { - object value = this.ConvertValue(dataType, offsetBuffer[..(int)size], numberOfComponents > 1 || exifValue.IsArray); + object? value = this.ConvertValue(dataType, offsetBuffer[..(int)size], numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } - private void Add(IList values, IExifValue exif, object value) + private void Add(IList values, IExifValue exif, object? value) { if (!exif.TrySetValue(value)) { @@ -510,7 +509,7 @@ internal abstract class BaseExifReader private void AddInvalidTag(ExifTag tag) => (this.invalidTags ??= new List()).Add(tag); - private void AddSubIfd(object val) + private void AddSubIfd(object? val) => (this.subIfds ??= new List()).Add(Convert.ToUInt64(val, CultureInfo.InvariantCulture)); private void Seek(ulong pos) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs index b9e74e246..da5214087 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Reflection; @@ -29,10 +28,10 @@ internal sealed class ExifTagDescriptionAttribute : Attribute /// /// The . /// - public static string GetDescription(ExifTag tag, object value) + public static string? GetDescription(ExifTag tag, object? value) { var tagValue = (ExifTagValue)(ushort)tag; - FieldInfo field = typeof(ExifTagValue).GetField(tagValue.ToString(), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + FieldInfo? field = typeof(ExifTagValue).GetField(tagValue.ToString(), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (field is null) { @@ -41,11 +40,11 @@ internal sealed class ExifTagDescriptionAttribute : Attribute foreach (CustomAttributeData customAttribute in field.CustomAttributes) { - object attributeValue = customAttribute.ConstructorArguments[0].Value; + object? attributeValue = customAttribute.ConstructorArguments[0].Value; if (Equals(attributeValue, value)) { - return (string)customAttribute.ConstructorArguments[1].Value; + return (string?)customAttribute.ConstructorArguments[1].Value; } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index d156cbee3..26442b2c4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; @@ -16,7 +15,7 @@ internal sealed class ExifWriter /// private readonly ExifParts allowedParts; private readonly IList values; - private List dataOffsets; + private List? dataOffsets; private readonly List ifdValues; private readonly List exifValues; private readonly List gpsValues; @@ -45,8 +44,8 @@ internal sealed class ExifWriter { const uint startIndex = 0; - IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset); - IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset); + IExifValue? exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset); + IExifValue? gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset); uint ifdLength = GetLength(this.ifdValues); uint exifLength = GetLength(this.exifValues); @@ -160,7 +159,7 @@ internal sealed class ExifWriter return offset + 4; } - private static IExifValue GetOffsetValue(List ifdValues, List values, ExifTag offset) + private static IExifValue? GetOffsetValue(List ifdValues, List values, ExifTag offset) { int index = -1; @@ -179,7 +178,9 @@ internal sealed class ExifWriter return ifdValues[index]; } - ExifValue result = ExifValues.Create(offset); + ExifValue? result = ExifValues.Create(offset); + Guard.NotNull(result); + ifdValues.Add(result); return result; @@ -219,7 +220,7 @@ internal sealed class ExifWriter private static bool HasValue(IExifValue exifValue) { - object value = exifValue.GetValue(); + object? value = exifValue.GetValue(); if (value is null) { return false; @@ -270,11 +271,11 @@ internal sealed class ExifWriter internal static uint GetNumberOfComponents(IExifValue exifValue) { - object value = exifValue.GetValue(); + object? value = exifValue.GetValue(); if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) { - return (uint)ExifUcs2StringHelpers.Ucs2Encoding.GetByteCount((string)value); + return (uint)ExifUcs2StringHelpers.Ucs2Encoding.GetByteCount((string?)value!); } if (value is EncodedString encodedString) @@ -284,7 +285,7 @@ internal sealed class ExifWriter if (exifValue.DataType == ExifDataType.Ascii) { - return (uint)ExifConstants.DefaultEncoding.GetByteCount((string)value) + 1; + return (uint)ExifConstants.DefaultEncoding.GetByteCount((string?)value!) + 1; } if (value is Array arrayValue) @@ -298,7 +299,7 @@ internal sealed class ExifWriter private static int WriteArray(IExifValue value, Span destination, int offset) { int newOffset = offset; - foreach (object obj in (Array)value.GetValue()) + foreach (object obj in (Array)value.GetValue()!) { newOffset = WriteValue(value.DataType, obj, destination, newOffset); } @@ -308,7 +309,7 @@ internal sealed class ExifWriter private int WriteData(uint startIndex, List values, Span destination, int offset) { - if (this.dataOffsets.Count == 0) + if (this.dataOffsets is null || this.dataOffsets.Count == 0) { return offset; } @@ -428,7 +429,8 @@ internal sealed class ExifWriter internal static int WriteValue(IExifValue exifValue, Span destination, int offset) { - object value = exifValue.GetValue(); + object? value = exifValue.GetValue(); + Guard.NotNull(value); if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs index 97f795c0f..64b8d2313 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -23,11 +22,11 @@ internal abstract class ExifArrayValue : ExifValue, IExifValue true; - public TValueType[] Value { get; set; } + public TValueType[]? Value { get; set; } - public override object GetValue() => this.Value; + public override object? GetValue() => this.Value; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (value is null) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs index 910da27da..eb69c4389 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs @@ -20,7 +20,7 @@ internal sealed class ExifByte : ExifValue protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs index 532d69395..4320cb5e8 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs @@ -16,7 +16,7 @@ internal sealed class ExifByteArray : ExifArrayValue public override ExifDataType DataType { get; } - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs index e8a2699d8..1d24a7dfb 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs @@ -26,7 +26,7 @@ internal sealed class ExifDouble : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs index 317f8d771..cce7cf3e8 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs @@ -24,7 +24,7 @@ internal sealed class ExifEncodedString : ExifValue protected override string StringValue => this.Value.Text; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs index 904a9ee02..bfe5871e8 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs @@ -21,7 +21,7 @@ internal sealed class ExifFloat : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs index 4fb5e8092..8f185ea36 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs @@ -26,7 +26,7 @@ internal sealed class ExifLong : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs index 9f2503144..13feb6ad6 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs @@ -26,7 +26,7 @@ internal sealed class ExifLong8 : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index ff48fc774..b7756e62b 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -34,7 +34,7 @@ internal sealed class ExifLong8Array : ExifArrayValue } } - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs index 780f389ab..a78107317 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs @@ -32,7 +32,7 @@ internal sealed class ExifNumber : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 6b5aafbad..1162c25ea 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -34,7 +34,7 @@ internal sealed class ExifNumberArray : ExifArrayValue } } - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs index 958c6f834..c25f0f5dc 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs @@ -26,7 +26,7 @@ internal sealed class ExifRational : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs index 0114a7fbd..ac6453edc 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs @@ -22,7 +22,7 @@ internal sealed class ExifRationalArray : ExifArrayValue public override ExifDataType DataType => ExifDataType.Rational; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs index 68932b3b8..d35b21422 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs @@ -26,7 +26,7 @@ internal sealed class ExifShort : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs index 42c919bb7..a205e77de 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs @@ -22,7 +22,7 @@ internal sealed class ExifShortArray : ExifArrayValue public override ExifDataType DataType => ExifDataType.Short; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs index c9bf7a820..95a239b70 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs @@ -21,7 +21,7 @@ internal sealed class ExifSignedByte : ExifValue protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs index 72fd0b65a..6726febef 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs @@ -21,7 +21,7 @@ internal sealed class ExifSignedShort : ExifValue protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs index 464105b3b..8023fb8bc 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs @@ -17,7 +17,7 @@ internal sealed class ExifSignedShortArray : ExifArrayValue public override ExifDataType DataType => ExifDataType.SignedShort; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs index 1eb712bca..562583dae 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs @@ -24,9 +24,9 @@ internal sealed class ExifString : ExifValue public override ExifDataType DataType => ExifDataType.Ascii; - protected override string StringValue => this.Value; + protected override string? StringValue => this.Value; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs index 9e7c96dac..88c3e816e 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs @@ -22,11 +22,11 @@ internal sealed class ExifUcs2String : ExifValue public override ExifDataType DataType => ExifDataType.Byte; - protected override string StringValue => this.Value; + protected override string? StringValue => this.Value; - public override object GetValue() => this.Value; + public override object? GetValue() => this.Value; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (base.TrySetValue(value)) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs index b28e404df..eacb41cfb 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; @@ -28,7 +27,7 @@ internal abstract class ExifValue : IExifValue, IEquatable else { // All array types are value types so Clone() is sufficient here. - var array = (Array)other.GetValue(); + Array? array = (Array?)other.GetValue(); this.TrySetValue(array?.Clone()); } } @@ -43,7 +42,7 @@ internal abstract class ExifValue : IExifValue, IEquatable public static bool operator !=(ExifValue left, ExifTag right) => !Equals(left, right); - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is null) { @@ -69,14 +68,14 @@ internal abstract class ExifValue : IExifValue, IEquatable } [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(ExifTag other) => this.Tag.Equals(other); + public bool Equals(ExifTag? other) => this.Tag.Equals(other); [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => HashCode.Combine(this.Tag, this.GetValue()); - public abstract object GetValue(); + public abstract object? GetValue(); - public abstract bool TrySetValue(object value); + public abstract bool TrySetValue(object? value); public abstract IExifValue DeepClone(); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index 783da4025..8aa54c3a4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -1,18 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; internal static partial class ExifValues { - public static ExifValue Create(ExifTagValue tag) => (ExifValue)CreateValue(tag); + public static ExifValue? Create(ExifTagValue tag) => (ExifValue?)CreateValue(tag); - public static ExifValue Create(ExifTag tag) => (ExifValue)CreateValue((ExifTagValue)(ushort)tag); + public static ExifValue? Create(ExifTag tag) => (ExifValue?)CreateValue((ExifTagValue)(ushort)tag); - public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, ulong numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); + public static ExifValue? Create(ExifTagValue tag, ExifDataType dataType, ulong numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); - public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, bool isArray) + public static ExifValue? Create(ExifTagValue tag, ExifDataType dataType, bool isArray) { switch (dataType) { @@ -49,7 +48,7 @@ internal static partial class ExifValues } } - private static object CreateValue(ExifTagValue tag) + private static object? CreateValue(ExifTagValue tag) { switch (tag) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs index 0face8401..6442f2684 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -21,16 +20,16 @@ internal abstract class ExifValue : ExifValue, IExifValue /// Gets the value of the current instance as a string. /// - protected abstract string StringValue { get; } + protected abstract string? StringValue { get; } - public override object GetValue() => this.Value; + public override object? GetValue() => this.Value; - public override bool TrySetValue(object value) + public override bool TrySetValue(object? value) { if (value is null) { @@ -49,14 +48,9 @@ internal abstract class ExifValue : ExifValue, IExifValue /// Gets the value of this exif value. /// /// The value of this exif value. - object GetValue(); + object? GetValue(); /// /// Sets the value of this exif value. /// /// The value of this exif value. /// A value indicating whether the value could be set. - bool TrySetValue(object value); + bool TrySetValue(object? value); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs index d49e80ea0..ec84b78f7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs @@ -12,5 +12,5 @@ public interface IExifValue : IExifValue /// /// Gets or sets the value. /// - TValueType Value { get; set; } + TValueType? Value { get; set; } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs index 6952e561f..6d70bd379 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs @@ -88,7 +88,7 @@ internal class AutoOrientProcessor : ImageProcessor return ExifOrientationMode.Unknown; } - IExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + IExifValue? value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); if (value is null) { return ExifOrientationMode.Unknown; From a39ac71837145bc78265e03faa6f67974d497341 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 21 Jan 2023 10:10:48 +0100 Subject: [PATCH 2/7] Change GetThumbnail to TryGetThumbnail --- .../Metadata/Profiles/Exif/ExifProfile.cs | 31 +++++++++++++------ .../Profiles/Exif/ExifProfileTests.cs | 7 +++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index 9a41fb6a4..ebe20cff6 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -124,36 +124,47 @@ public sealed class ExifProfile : IDeepCloneable /// /// Returns the thumbnail in the EXIF profile when available. /// + /// The thumbnail /// - /// The . + /// True, if there is a thumbnail otherwise false. /// - public Image? CreateThumbnail() => this.CreateThumbnail(); + public bool TryCreateThumbnail([NotNullWhen(true)] out Image? image) + { + if (this.TryCreateThumbnail(out Image? innerimage)) + { + image = innerimage; + return true; + } + + image = null; + return false; + } /// /// Returns the thumbnail in the EXIF profile when available. /// /// The pixel format. - /// - /// The . - /// - public Image? CreateThumbnail() + /// The thumbnail. + /// True, if there is a thumbnail otherwise false. + public bool TryCreateThumbnail([NotNullWhen(true)] out Image? image) where TPixel : unmanaged, IPixel { this.InitializeValues(); - + image = null; if (this.thumbnailOffset == 0 || this.thumbnailLength == 0) { - return null; + return false; } if (this.data is null || this.data.Length < (this.thumbnailOffset + this.thumbnailLength)) { - return null; + return false; } using (var memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength)) { - return Image.Load(memStream); + image = Image.Load(memStream); + return true; } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 26f777ae7..873bc9ca2 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -345,13 +345,14 @@ public class ExifProfileTests ExifProfile profile = GetExifProfile(); TestProfile(profile); - - using Image thumbnail = profile.CreateThumbnail(); + bool retVal = profile.TryCreateThumbnail(out Image thumbnail); + Assert.True(retVal); Assert.NotNull(thumbnail); Assert.Equal(256, thumbnail.Width); Assert.Equal(170, thumbnail.Height); - using Image genericThumbnail = profile.CreateThumbnail(); + retVal = profile.TryCreateThumbnail(out Image genericThumbnail); + Assert.True(retVal); Assert.NotNull(genericThumbnail); Assert.Equal(256, genericThumbnail.Width); Assert.Equal(170, genericThumbnail.Height); From 06f413107f15d19be085f222088acade7dc60e78 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 21 Jan 2023 13:11:52 +0100 Subject: [PATCH 3/7] Convert to TryGetValue --- .../Common/Helpers/UnitConverter.cs | 9 +- .../Formats/Jpeg/JpegDecoderCore.cs | 7 +- .../Formats/Tiff/TiffBitsPerSample.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 32 +++++-- .../Tiff/TiffDecoderMetadataCreator.cs | 22 +++-- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 94 +++++++++++++++---- .../Formats/Tiff/TiffEncoderCore.cs | 2 - .../Formats/Tiff/TiffFrameMetadata.cs | 26 +++-- .../Formats/Tiff/TiffThrowHelper.cs | 9 ++ .../Metadata/Profiles/Exif/ExifProfile.cs | 19 ++-- .../Linear/AutoOrientProcessor{TPixel}.cs | 3 +- .../Transforms/TransformProcessorHelpers.cs | 4 +- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 1 + .../Formats/Png/PngMetadataTests.cs | 1 + .../Formats/Tiff/BigTiffDecoderTests.cs | 1 + .../Formats/Tiff/TiffEncoderBaseTester.cs | 1 + .../Formats/Tiff/TiffMetadataTests.cs | 9 +- .../Formats/WebP/WebpMetaDataTests.cs | 1 + .../Metadata/ImageMetadataTests.cs | 1 + .../Profiles/Exif/ExifProfileTests.cs | 5 +- .../Exif/ExifTagDescriptionAttributeTests.cs | 1 + .../Metadata/Profiles/Exif/ExifValueTests.cs | 4 +- .../Transforms/TransformsHelpersTest.cs | 1 + .../TestUtilities/ExifProfileExtensions.cs | 18 ++++ 24 files changed, 207 insertions(+), 66 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/ExifProfileExtensions.cs diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs index 983d86a71..45dbe41cb 100644 --- a/src/ImageSharp/Common/Helpers/UnitConverter.cs +++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs @@ -91,10 +91,13 @@ internal static class UnitConverter [MethodImpl(InliningOptions.ShortMethod)] public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile) { - IExifValue? resolution = profile.GetValue(ExifTag.ResolutionUnit); + if (profile.TryGetValue(ExifTag.ResolutionUnit, out IExifValue? resolution)) + { + // EXIF is 1, 2, 3 so we minus "1" off the result. + return (PixelResolutionUnit)(byte)(resolution.Value - 1); + } - // EXIF is 1, 2, 3 so we minus "1" off the result. - return resolution is null ? DefaultResolutionUnit : (PixelResolutionUnit)(byte)(resolution.Value - 1); + return DefaultResolutionUnit; } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 511ec1ba1..77ea4e86f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -705,9 +705,12 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals private double GetExifResolutionValue(ExifTag tag) { - IExifValue resolution = this.Metadata.ExifProfile.GetValue(tag); + if (this.Metadata.ExifProfile.TryGetValue(tag, out IExifValue resolution)) + { + return resolution.Value.ToDouble(); + } - return resolution is null ? 0 : resolution.Value.ToDouble(); + return 0; } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index fb35ef2de..382faa387 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -82,7 +82,7 @@ public readonly struct TiffBitsPerSample : IEquatable /// The value to parse. /// The tiff bits per sample. /// True, if the value could be parsed. - public static bool TryParse(ushort[] value, out TiffBitsPerSample sample) + public static bool TryParse(ushort[]? value, out TiffBitsPerSample sample) { if (value is null || value.Length == 0) { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e74c53e22..87784d1cb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -276,9 +276,15 @@ internal class TiffDecoderCore : IImageDecoderInternals private void DecodeImageWithStrips(ExifProfile tags, ImageFrame frame, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null - ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value - : TiffConstants.RowsPerStripInfinity; + int rowsPerStrip; + if (tags.TryGetValue(ExifTag.RowsPerStrip, out IExifValue value)) + { + rowsPerStrip = (int)value.Value; + } + else + { + rowsPerStrip = TiffConstants.RowsPerStripInfinity; + } Array stripOffsetsArray = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue(); Array stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue(); @@ -320,8 +326,18 @@ internal class TiffDecoderCore : IImageDecoderInternals int width = pixels.Width; int height = pixels.Height; - int tileWidth = (int)tags.GetValue(ExifTag.TileWidth).Value; - int tileLength = (int)tags.GetValue(ExifTag.TileLength).Value; + if (!tags.TryGetValue(ExifTag.TileWidth, out IExifValue valueWidth)) + { + ArgumentNullException.ThrowIfNull(valueWidth); + } + + if (!tags.TryGetValue(ExifTag.TileWidth, out IExifValue valueLength)) + { + ArgumentNullException.ThrowIfNull(valueLength); + } + + int tileWidth = (int)valueWidth.Value; + int tileLength = (int)valueLength.Value; int tilesAcross = (width + tileWidth - 1) / tileWidth; int tilesDown = (height + tileLength - 1) / tileLength; @@ -775,8 +791,7 @@ internal class TiffDecoderCore : IImageDecoderInternals /// The image width. private static int GetImageWidth(ExifProfile exifProfile) { - IExifValue width = exifProfile.GetValue(ExifTag.ImageWidth); - if (width == null) + if (!exifProfile.TryGetValue(ExifTag.ImageWidth, out IExifValue width)) { TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); } @@ -793,8 +808,7 @@ internal class TiffDecoderCore : IImageDecoderInternals /// The image height. private static int GetImageHeight(ExifProfile exifProfile) { - IExifValue height = exifProfile.GetValue(ExifTag.ImageLength); - if (height == null) + if (!exifProfile.TryGetValue(ExifTag.ImageLength, out IExifValue height)) { TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageLength"); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index b6ce718f3..1a6c9dd7d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -38,14 +38,12 @@ internal static class TiffDecoderMetadataCreator frameMetaData.IptcProfile = new IptcProfile(iptcBytes); } - IExifValue xmpProfileBytes = frameMetaData.ExifProfile.GetValue(ExifTag.XMP); - if (xmpProfileBytes != null) + if (frameMetaData.ExifProfile.TryGetValue(ExifTag.XMP, out IExifValue xmpProfileBytes)) { frameMetaData.XmpProfile = new XmpProfile(xmpProfileBytes.Value); } - IExifValue iccProfileBytes = frameMetaData.ExifProfile.GetValue(ExifTag.IccProfile); - if (iccProfileBytes != null) + if (frameMetaData.ExifProfile.TryGetValue(ExifTag.IccProfile, out IExifValue iccProfileBytes)) { frameMetaData.IccProfile = new IccProfile(iccProfileBytes.Value); } @@ -70,16 +68,20 @@ internal static class TiffDecoderMetadataCreator private static void SetResolution(ImageMetadata imageMetaData, ExifProfile exifProfile) { imageMetaData.ResolutionUnits = exifProfile != null ? UnitConverter.ExifProfileToResolutionUnit(exifProfile) : PixelResolutionUnit.PixelsPerInch; - double? horizontalResolution = exifProfile?.GetValue(ExifTag.XResolution)?.Value.ToDouble(); - if (horizontalResolution != null) + + if (exifProfile is null) + { + return; + } + + if (exifProfile.TryGetValue(ExifTag.XResolution, out IExifValue horizontalResolution)) { - imageMetaData.HorizontalResolution = horizontalResolution.Value; + imageMetaData.HorizontalResolution = horizontalResolution.Value.ToDouble(); } - double? verticalResolution = exifProfile?.GetValue(ExifTag.YResolution)?.Value.ToDouble(); - if (verticalResolution != null) + if (exifProfile.TryGetValue(ExifTag.YResolution, out IExifValue verticalResolution)) { - imageMetaData.VerticalResolution = verticalResolution.Value; + imageMetaData.VerticalResolution = verticalResolution.Value.ToDouble(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index f20263602..a13fb58de 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -42,7 +42,16 @@ internal static class TiffDecoderOptionsParser } } - TiffFillOrder fillOrder = (TiffFillOrder?)exifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst; + TiffFillOrder fillOrder; + if (exifProfile.TryGetValue(ExifTag.FillOrder, out IExifValue value)) + { + fillOrder = (TiffFillOrder)value.Value; + } + else + { + fillOrder = TiffFillOrder.MostSignificantBitFirst; + } + if (fillOrder == TiffFillOrder.LeastSignificantBitFirst && frameMetadata.BitsPerPixel != TiffBitsPerPixel.Bit1) { TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is only supported in combination with 1bit per pixel bicolor tiff's."); @@ -53,10 +62,10 @@ internal static class TiffDecoderOptionsParser TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported."); } - TiffSampleFormat[] sampleFormats = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); TiffSampleFormat? sampleFormat = null; - if (sampleFormats != null) + if (exifProfile.TryGetValue(ExifTag.SampleFormat, out var formatValue)) { + TiffSampleFormat[] sampleFormats = formatValue.Value.Select(a => (TiffSampleFormat)a).ToArray(); sampleFormat = sampleFormats[0]; foreach (TiffSampleFormat format in sampleFormats) { @@ -67,7 +76,12 @@ internal static class TiffDecoderOptionsParser } } - ushort[] ycbcrSubSampling = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value; + ushort[] ycbcrSubSampling = null; + if (exifProfile.TryGetValue(ExifTag.YCbCrSubsampling, out IExifValue subSamplingValue)) + { + ycbcrSubSampling = subSamplingValue.Value; + } + if (ycbcrSubSampling != null && ycbcrSubSampling.Length != 2) { TiffThrowHelper.ThrowImageFormatException("Invalid YCbCrSubsampling, expected 2 values."); @@ -78,23 +92,52 @@ internal static class TiffDecoderOptionsParser TiffThrowHelper.ThrowImageFormatException("ChromaSubsampleVert shall always be less than or equal to ChromaSubsampleHoriz."); } - if (exifProfile.GetValue(ExifTag.StripRowCounts)?.Value != null) + if (exifProfile.TryGetValue(ExifTag.StripRowCounts, out _)) { TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported."); } - options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; + if (exifProfile.TryGetValue(ExifTag.PlanarConfiguration, out IExifValue planarValue)) + { + options.PlanarConfiguration = (TiffPlanarConfiguration)planarValue.Value; + } + else + { + options.PlanarConfiguration = DefaultPlanarConfiguration; + } + options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None; options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb; options.SampleFormat = sampleFormat ?? TiffSampleFormat.UnsignedInteger; options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0); - options.ReferenceBlackAndWhite = exifProfile.GetValue(ExifTag.ReferenceBlackWhite)?.Value; - options.YcbcrCoefficients = exifProfile.GetValue(ExifTag.YCbCrCoefficients)?.Value; - options.YcbcrSubSampling = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value; + + if (exifProfile.TryGetValue(ExifTag.ReferenceBlackWhite, out IExifValue blackWhiteValue)) + { + options.ReferenceBlackAndWhite = blackWhiteValue.Value; + } + + if (exifProfile.TryGetValue(ExifTag.YCbCrCoefficients, out IExifValue coefficientsValue)) + { + options.YcbcrCoefficients = coefficientsValue.Value; + } + + if (exifProfile.TryGetValue(ExifTag.YCbCrSubsampling, out IExifValue ycbrSubSamplingValue)) + { + options.YcbcrSubSampling = ycbrSubSamplingValue.Value; + } + options.FillOrder = fillOrder; - options.JpegTables = exifProfile.GetValue(ExifTag.JPEGTables)?.Value; - options.OldJpegCompressionStartOfImageMarker = exifProfile.GetValue(ExifTag.JPEGInterchangeFormat)?.Value; + + if (exifProfile.TryGetValue(ExifTag.JPEGTables, out IExifValue jpegTablesValue)) + { + options.JpegTables = jpegTablesValue.Value; + } + + if (exifProfile.TryGetValue(ExifTag.JPEGInterchangeFormat, out IExifValue jpegInterchangeFormatValue)) + { + options.OldJpegCompressionStartOfImageMarker = jpegInterchangeFormatValue.Value; + } options.ParseColorType(exifProfile); options.ParseCompression(frameMetadata.Compression, exifProfile); @@ -394,9 +437,9 @@ internal static class TiffDecoderOptionsParser case TiffPhotometricInterpretation.PaletteColor: { - options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; - if (options.ColorMap != null) + if (exifProfile.TryGetValue(ExifTag.ColorMap, out IExifValue value)) { + options.ColorMap = value.Value; if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); @@ -414,7 +457,11 @@ internal static class TiffDecoderOptionsParser case TiffPhotometricInterpretation.YCbCr: { - options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; + if (exifProfile.TryGetValue(ExifTag.ColorMap, out IExifValue value)) + { + options.ColorMap = value.Value; + } + if (options.BitsPerSample.Channels != 3) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported for YCbCr images."); @@ -508,7 +555,15 @@ internal static class TiffDecoderOptionsParser case TiffCompression.CcittGroup3Fax: { options.CompressionType = TiffDecoderCompressionType.T4; - options.FaxCompressionOptions = exifProfile.GetValue(ExifTag.T4Options) != null ? (FaxCompressionOptions)exifProfile.GetValue(ExifTag.T4Options).Value : FaxCompressionOptions.None; + + if (exifProfile.TryGetValue(ExifTag.T4Options, out IExifValue t4OptionsValue)) + { + options.FaxCompressionOptions = (FaxCompressionOptions)t4OptionsValue.Value; + } + else + { + options.FaxCompressionOptions = FaxCompressionOptions.None; + } break; } @@ -516,7 +571,14 @@ internal static class TiffDecoderOptionsParser case TiffCompression.CcittGroup4Fax: { options.CompressionType = TiffDecoderCompressionType.T6; - options.FaxCompressionOptions = exifProfile.GetValue(ExifTag.T4Options) != null ? (FaxCompressionOptions)exifProfile.GetValue(ExifTag.T4Options).Value : FaxCompressionOptions.None; + if (exifProfile.TryGetValue(ExifTag.T4Options, out IExifValue t4OptionsValue)) + { + options.FaxCompressionOptions = (FaxCompressionOptions)t4OptionsValue.Value; + } + else + { + options.FaxCompressionOptions = FaxCompressionOptions.None; + } break; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 8da2c26de..3a4e71d3e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -164,8 +164,6 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals { cancellationToken.ThrowIfCancellationRequested(); - TiffNewSubfileType subfileType = (TiffNewSubfileType)(frame.Metadata.ExifProfile?.GetValue(ExifTag.SubfileType)?.Value ?? (int)TiffNewSubfileType.FullImage); - ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker); metadataImage = null; } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 956213c23..5d0b85bf2 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -78,15 +77,30 @@ public class TiffFrameMetadata : IDeepCloneable { if (profile != null) { - if (TiffBitsPerSample.TryParse(profile.GetValue(ExifTag.BitsPerSample)?.Value, out TiffBitsPerSample bitsPerSample)) + if (profile.TryGetValue(ExifTag.BitsPerSample, out IExifValue? bitsPerSampleValue)) { - meta.BitsPerSample = bitsPerSample; + if (TiffBitsPerSample.TryParse(bitsPerSampleValue.Value, out TiffBitsPerSample bitsPerSample)) + { + meta.BitsPerSample = bitsPerSample; + } } meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); - meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value; - meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; - meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; + + if (profile.TryGetValue(ExifTag.Compression, out IExifValue? compressionValue)) + { + meta.Compression = (TiffCompression)compressionValue.Value; + } + + if (profile.TryGetValue(ExifTag.PhotometricInterpretation, out IExifValue? photometricInterpretationValue)) + { + meta.PhotometricInterpretation = (TiffPhotometricInterpretation)photometricInterpretationValue.Value; + } + + if (profile.TryGetValue(ExifTag.Predictor, out IExifValue? predictorValue)) + { + meta.Predictor = (TiffPredictor)predictorValue.Value; + } profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.Compression); diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index b2cc638ba..3957ccb02 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -1,21 +1,30 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; + namespace SixLabors.ImageSharp.Formats.Tiff; internal static class TiffThrowHelper { + [DoesNotReturn] public static Exception ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); + [DoesNotReturn] public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); + [DoesNotReturn] public static Exception NotSupportedCompressor(string compressionType) => throw new NotSupportedException($"Not supported encoder compression method: {compressionType}"); + [DoesNotReturn] public static Exception InvalidColorType(string colorType) => throw new NotSupportedException($"Invalid color type: {colorType}"); + [DoesNotReturn] public static Exception ThrowInvalidHeader() => throw new ImageFormatException("Invalid TIFF file header."); + [DoesNotReturn] public static void ThrowNotSupported(string message) => throw new NotSupportedException(message); + [DoesNotReturn] public static void ThrowArgumentException(string message) => throw new ArgumentException(message); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index ebe20cff6..e14c88229 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -172,12 +172,21 @@ public sealed class ExifProfile : IDeepCloneable /// Returns the value with the specified tag. /// /// The tag of the exif value. - /// The value with the specified tag. + /// The value with the specified tag. + /// True when found, otherwise false /// The data type of the tag. - public IExifValue? GetValue(ExifTag tag) + public bool TryGetValue(ExifTag tag, [NotNullWhen(true)] out IExifValue? exifValue) { IExifValue? value = this.GetValueInternal(tag); - return value is null ? null : (IExifValue)value; + + if (value is null) + { + exifValue = null; + return false; + } + + exifValue = (IExifValue)value; + return true; } /// @@ -291,9 +300,7 @@ public sealed class ExifProfile : IDeepCloneable private void SyncResolution(ExifTag tag, double resolution) { - IExifValue? value = this.GetValue(tag); - - if (value is null) + if (!this.TryGetValue(tag, out IExifValue? value)) { return; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs index 6d70bd379..0ad684212 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs @@ -88,8 +88,7 @@ internal class AutoOrientProcessor : ImageProcessor return ExifOrientationMode.Unknown; } - IExifValue? value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); - if (value is null) + if (!source.Metadata.ExifProfile.TryGetValue(ExifTag.Orientation, out IExifValue? value)) { return ExifOrientationMode.Unknown; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs index 172d7d677..0bb4920f0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -26,12 +26,12 @@ internal static class TransformProcessorHelpers } // Only set the value if it already exists. - if (profile.GetValue(ExifTag.PixelXDimension) != null) + if (profile.TryGetValue(ExifTag.PixelXDimension, out _)) { profile.SetValue(ExifTag.PixelXDimension, image.Width); } - if (profile.GetValue(ExifTag.PixelYDimension) != null) + if (profile.TryGetValue(ExifTag.PixelYDimension, out _)) { profile.SetValue(ExifTag.PixelYDimension, image.Height); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 7e0b1de82..9bff30b6f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index 9653a0656..d7a353665 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Formats.Png; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 5c110844c..8e90b1dd5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.BigTiff; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs index 6da0d4fd8..2a822e705 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 2a809e895..51c0b6ef7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using static SixLabors.ImageSharp.Tests.TestImages.Tiff; namespace SixLabors.ImageSharp.Tests.Formats.Tiff; @@ -167,9 +168,9 @@ public class TiffMetadataTests Assert.Equal("Make", exifProfile.GetValue(ExifTag.Make).Value); Assert.Equal("Model", exifProfile.GetValue(ExifTag.Model).Value); Assert.Equal("ImageSharp", exifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(exifProfile.GetValue(ExifTag.DateTime)?.Value); + Assert.Null(exifProfile.GetValue(ExifTag.DateTime, false)?.Value); Assert.Equal("Artist", exifProfile.GetValue(ExifTag.Artist).Value); - Assert.Null(exifProfile.GetValue(ExifTag.HostComputer)?.Value); + Assert.Null(exifProfile.GetValue(ExifTag.HostComputer, false)?.Value); Assert.Equal("Copyright", exifProfile.GetValue(ExifTag.Copyright).Value); Assert.Equal(4, exifProfile.GetValue(ExifTag.Rating).Value); Assert.Equal(75, exifProfile.GetValue(ExifTag.RatingPercent).Value); @@ -178,9 +179,9 @@ public class TiffMetadataTests Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.YResolution).Value); Assert.Equal(new Number[] { 8u }, exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer()); Assert.Equal(new Number[] { 285u }, exifProfile.GetValue(ExifTag.StripByteCounts)?.Value, new NumberComparer()); - Assert.Null(exifProfile.GetValue(ExifTag.ExtraSamples)?.Value); + Assert.Null(exifProfile.GetValue(ExifTag.ExtraSamples, false)?.Value); Assert.Equal(32u, exifProfile.GetValue(ExifTag.RowsPerStrip).Value); - Assert.Null(exifProfile.GetValue(ExifTag.SampleFormat)); + Assert.Null(exifProfile.GetValue(ExifTag.SampleFormat, false)); Assert.Equal(PixelResolutionUnit.PixelsPerInch, UnitConverter.ExifProfileToResolutionUnit(exifProfile)); ushort[] colorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; Assert.NotNull(colorMap); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs index c0c2e375f..ab8fef60f 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Webp; diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs index 9b1e42d01..330b70147 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs @@ -4,6 +4,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Metadata; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 873bc9ca2..8072bebd7 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif; @@ -255,7 +256,7 @@ public class ExifProfileTests xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); Assert.Equal(new Rational(150.0), xResolution.Value); - referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite, false); Assert.Null(referenceBlackWhite); latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); @@ -300,7 +301,7 @@ public class ExifProfileTests Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); - Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); + Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace, false)); Assert.Equal(profileCount - 1, image.Metadata.ExifProfile.Values.Count); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index d2198f9ca..1154b3439 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs index 548329250..d04aa5e76 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs @@ -23,7 +23,9 @@ public class ExifValueTests { Assert.NotNull(this.profile); - return this.profile.GetValue(ExifTag.Software); + this.profile.TryGetValue(ExifTag.Software, out IExifValue value); + + return value; } [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index 594eb7e7c..21b92a01e 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -4,6 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Processing.Transforms; diff --git a/tests/ImageSharp.Tests/TestUtilities/ExifProfileExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/ExifProfileExtensions.cs new file mode 100644 index 000000000..259b6a933 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ExifProfileExtensions.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata.Profiles.Exif; + +namespace SixLabors.ImageSharp.Tests.TestUtilities; + +public static class ExifProfileExtensions +{ + public static IExifValue GetValue(this ExifProfile exifProfileInput, ExifTag tag, bool assertTrue = true) + { + bool boolValue = exifProfileInput.TryGetValue(tag, out IExifValue value); + + Assert.Equal(assertTrue, boolValue); + + return value; + } +} From 4949c348faaf1bc61ac764e4af7153703d383823 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 21 Jan 2023 13:20:14 +0100 Subject: [PATCH 4/7] Change CodePagesEncodingProvider to Encoding --- .../Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs index bfe991f83..f743a2e24 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs @@ -26,8 +26,7 @@ internal static class ExifEncodedStringHelpers // 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990) // https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-6.0 - private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(20932) ?? - throw new InvalidOperationException(); + private static Encoding JIS0208Encoding => Encoding.GetEncoding(20932); public static bool IsEncodedString(ExifTagValue tag) => tag switch { From ff54427090a62fc9c5b941c1dc7c1d96c2109a67 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 21 Jan 2023 13:44:53 +0100 Subject: [PATCH 5/7] Fix Encoding --- .../Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs index f743a2e24..e9f46731c 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs @@ -26,7 +26,14 @@ internal static class ExifEncodedStringHelpers // 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990) // https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-6.0 - private static Encoding JIS0208Encoding => Encoding.GetEncoding(20932); + private static Encoding JIS0208Encoding + { + get + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + return Encoding.GetEncoding(20932); + } + } public static bool IsEncodedString(ExifTagValue tag) => tag switch { From b5c6c22c21a91287034b2b68431334fdba1eb296 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 23 Jan 2023 20:36:39 +0100 Subject: [PATCH 6/7] Changes from Review * Throw InvalidImageContentException * Change GetDesctiption to TryGetDescription * Do not throw when creating exifvalue and adding it to ifdValues --- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs | 3 +++ .../Exif/ExifTagDescriptionAttribute.cs | 18 ++++++++++++------ .../Metadata/Profiles/Exif/ExifWriter.cs | 6 ++++-- .../Exif/Values/ExifValue{TValueType}.cs | 6 +----- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 87784d1cb..8333dbf31 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -793,7 +793,7 @@ internal class TiffDecoderCore : IImageDecoderInternals { if (!exifProfile.TryGetValue(ExifTag.ImageWidth, out IExifValue width)) { - TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); + TiffThrowHelper.ThrowInvalidImageContentException("The TIFF image frame is missing the ImageWidth"); } DebugGuard.MustBeLessThanOrEqualTo((ulong)width.Value, (ulong)int.MaxValue, nameof(ExifTag.ImageWidth)); diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index 3957ccb02..4a03bd44f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -10,6 +10,9 @@ internal static class TiffThrowHelper [DoesNotReturn] public static Exception ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); + [DoesNotReturn] + public static Exception ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); + [DoesNotReturn] public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs index da5214087..7a520343a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -25,17 +26,20 @@ internal sealed class ExifTagDescriptionAttribute : Attribute /// /// The tag. /// The value. + /// The description. /// - /// The . + /// True when description was found /// - public static string? GetDescription(ExifTag tag, object? value) + public static bool TryGetDescription(ExifTag tag, object? value, [NotNullWhen(true)] out string? description) { - var tagValue = (ExifTagValue)(ushort)tag; + ExifTagValue tagValue = (ExifTagValue)(ushort)tag; FieldInfo? field = typeof(ExifTagValue).GetField(tagValue.ToString(), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + description = null; + if (field is null) { - return null; + return false; } foreach (CustomAttributeData customAttribute in field.CustomAttributes) @@ -44,10 +48,12 @@ internal sealed class ExifTagDescriptionAttribute : Attribute if (Equals(attributeValue, value)) { - return (string?)customAttribute.ConstructorArguments[1].Value; + description = (string?)customAttribute.ConstructorArguments[1].Value; + + return description is not null; } } - return null; + return false; } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index 26442b2c4..76fb2ff39 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -179,9 +179,11 @@ internal sealed class ExifWriter } ExifValue? result = ExifValues.Create(offset); - Guard.NotNull(result); - ifdValues.Add(result); + if (result is not null) + { + ifdValues.Add(result); + } return result; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs index 6442f2684..e7a2fa5e5 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs @@ -48,9 +48,5 @@ internal abstract class ExifValue : ExifValue, IExifValue ExifTagDescriptionAttribute.TryGetDescription(this.Tag, this.Value, out string? description) ? description : this.StringValue; } From 54df7ab641df05975e895036259d49f85703607b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 26 Jan 2023 15:23:35 +1000 Subject: [PATCH 7/7] Safer cast, cleanup and comment --- .../Metadata/Profiles/Exif/ExifProfile.cs | 18 +++++++++--------- .../Metadata/Profiles/Exif/ExifReader.cs | 1 + .../Metadata/Profiles/Exif/ExifWriter.cs | 5 ++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index e14c88229..dd5792ae7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -79,7 +78,7 @@ public sealed class ExifProfile : IDeepCloneable this.InvalidTags = other.InvalidTags.Count > 0 ? new List(other.InvalidTags) - : (IReadOnlyList)Array.Empty(); + : Array.Empty(); if (other.values != null) { @@ -161,7 +160,7 @@ public sealed class ExifProfile : IDeepCloneable return false; } - using (var memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength)) + using (MemoryStream memStream = new(this.data, this.thumbnailOffset, this.thumbnailLength)) { image = Image.Load(memStream); return true; @@ -237,12 +236,12 @@ public sealed class ExifProfile : IDeepCloneable return Array.Empty(); } - var writer = new ExifWriter(this.values, this.Parts); + ExifWriter writer = new(this.values, this.Parts); return writer.GetData(); } /// - public ExifProfile DeepClone() => new ExifProfile(this); + public ExifProfile DeepClone() => new(this); /// /// Returns the value with the specified tag. @@ -267,6 +266,7 @@ public sealed class ExifProfile : IDeepCloneable /// /// The tag of the exif value. /// The value. + /// Newly created value is null. internal void SetValueInternal(ExifTag tag, object? value) { foreach (IExifValue exifValue in this.Values) @@ -281,7 +281,7 @@ public sealed class ExifProfile : IDeepCloneable ExifValue? newExifValue = ExifValues.Create(tag); if (newExifValue is null) { - throw new NotSupportedException(); + throw new NotSupportedException($"Newly created value for tag {tag} is null."); } newExifValue.TrySetValue(value); @@ -310,7 +310,7 @@ public sealed class ExifProfile : IDeepCloneable this.RemoveValue(value.Tag); } - var newResolution = new Rational(resolution, false); + Rational newResolution = new(resolution, false); this.SetValue(tag, newResolution); } @@ -328,13 +328,13 @@ public sealed class ExifProfile : IDeepCloneable return; } - var reader = new ExifReader(this.data); + ExifReader reader = new(this.data); this.values = reader.ReadValues(); this.InvalidTags = reader.InvalidTags.Count > 0 ? new List(reader.InvalidTags) - : (IReadOnlyList)Array.Empty(); + : Array.Empty(); this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailLength = (int)reader.ThumbnailLength; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 7b989c8d8..885db3a5e 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -22,6 +22,7 @@ internal class ExifReader : BaseExifReader public ExifReader(byte[] exifData, MemoryAllocator? allocator) : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData))), allocator) { + // TODO: We never call this constructor passing a non-null allocator. } /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index 76fb2ff39..1d2dca870 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -90,7 +90,7 @@ internal sealed class ExifWriter if (gpsLength > 0) { i = this.WriteHeaders(this.gpsValues, result, i); - i = this.WriteData(startIndex, this.gpsValues, result, i); + this.WriteData(startIndex, this.gpsValues, result, i); } return result; @@ -228,9 +228,8 @@ internal sealed class ExifWriter return false; } - if (exifValue.DataType == ExifDataType.Ascii) + if (exifValue.DataType == ExifDataType.Ascii && value is string stringValue) { - string stringValue = (string)value; return stringValue.Length > 0; }