Browse Source

Merge branch 'main' into icc-color-conversion

pull/1567/head
James Jackson-South 3 years ago
committed by GitHub
parent
commit
ed8091bc6e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/ImageSharp/Color/Color.cs
  2. 8
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  3. 18
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  4. 5
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
  5. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
  6. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs
  7. 9
      src/ImageSharp/Common/Helpers/UnitConverter.cs
  8. 10
      src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs
  9. 2
      src/ImageSharp/Compression/Zlib/Deflater.cs
  10. 12
      src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
  11. 9
      src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs
  12. 3
      src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs
  13. 2
      src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs
  14. 3
      src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs
  15. 6
      src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
  16. 7
      src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
  17. 5
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  18. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  19. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
  20. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  21. 19
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  22. 22
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
  23. 31
      src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
  24. 5
      src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
  25. 2
      src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
  26. 2
      src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
  27. 34
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  28. 22
      src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
  29. 94
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  30. 22
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  31. 20
      src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
  32. 26
      src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
  33. 12
      src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs
  34. 9
      src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
  35. 22
      src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs
  36. 11
      src/ImageSharp/Memory/Allocators/Internals/Gen2GcCallback.cs
  37. 3
      src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
  38. 15
      src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
  39. 7
      src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs
  40. 11
      src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs
  41. 2
      src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs
  42. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
  43. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs
  44. 6
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
  45. 10
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
  46. 8
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  47. 10
      src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
  48. 89
      src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
  49. 28
      src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
  50. 23
      src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
  51. 37
      src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
  52. 7
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs
  53. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs
  54. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs
  55. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs
  56. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs
  57. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs
  58. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs
  59. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs
  60. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs
  61. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs
  62. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs
  63. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs
  64. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs
  65. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs
  66. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs
  67. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs
  68. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs
  69. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs
  70. 4
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs
  71. 6
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs
  72. 11
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs
  73. 11
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs
  74. 20
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs
  75. 4
      src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs
  76. 2
      src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs
  77. 3
      src/ImageSharp/PixelFormats/PixelImplementations/A8.cs
  78. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs
  79. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs
  80. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs
  81. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs
  82. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs
  83. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs
  84. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs
  85. 3
      src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs
  86. 3
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs
  87. 3
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs
  88. 3
      src/ImageSharp/PixelFormats/PixelImplementations/L16.cs
  89. 3
      src/ImageSharp/PixelFormats/PixelImplementations/L8.cs
  90. 3
      src/ImageSharp/PixelFormats/PixelImplementations/La16.cs
  91. 3
      src/ImageSharp/PixelFormats/PixelImplementations/La32.cs
  92. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs
  93. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs
  94. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs
  95. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs
  96. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs
  97. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
  98. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs
  99. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs
  100. 7
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs

5
src/ImageSharp/Color/Color.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -20,7 +19,7 @@ namespace SixLabors.ImageSharp;
public readonly partial struct Color : IEquatable<Color> public readonly partial struct Color : IEquatable<Color>
{ {
private readonly Rgba64 data; private readonly Rgba64 data;
private readonly IPixel boxedHighPrecisionPixel; private readonly IPixel? boxedHighPrecisionPixel;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private Color(byte r, byte g, byte b, byte a) private Color(byte r, byte g, byte b, byte a)
@ -317,7 +316,7 @@ public readonly partial struct Color : IEquatable<Color>
} }
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) => obj is Color other && this.Equals(other); public override bool Equals(object? obj) => obj is Color other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

8
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -32,7 +32,8 @@ public partial class ColorSpaceConverter
return color; return color;
} }
return this.chromaticAdaptation.Transform(color, sourceWhitePoint, targetWhitePoint); // We know that chromaticAdaption is not null because performChromaticAdaption is checked
return this.chromaticAdaptation!.Transform(color, sourceWhitePoint, targetWhitePoint);
} }
/// <summary> /// <summary>
@ -128,11 +129,12 @@ public partial class ColorSpaceConverter
} }
// Conversion to XYZ // Conversion to XYZ
LinearRgbToCieXyzConverter converterToXYZ = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace); LinearRgbToCieXyzConverter converterToXYZ = GetLinearRgbToCieXyzConverter(color.WorkingSpace);
CieXyz unadapted = converterToXYZ.Convert(color); CieXyz unadapted = converterToXYZ.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = this.chromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint); // We know that chromaticAdaption is not null because performChromaticAdaption is checked
CieXyz adapted = this.chromaticAdaptation!.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion back to RGB // Conversion back to RGB
return this.cieXyzToLinearRgbConverter.Convert(adapted); return this.cieXyzToLinearRgbConverter.Convert(adapted);

18
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -11,9 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion;
/// </content> /// </content>
public partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new(); private static readonly ConcurrentDictionary<RgbWorkingSpace, LinearRgbToCieXyzConverter> ConverterCache = new();
private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter;
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="CieXyz"/>. /// Converts a <see cref="CieLab"/> into a <see cref="CieXyz"/>.
@ -327,7 +326,7 @@ public partial class ColorSpaceConverter
public CieXyz ToCieXyz(in LinearRgb color) public CieXyz ToCieXyz(in LinearRgb color)
{ {
// Conversion // Conversion
LinearRgbToCieXyzConverter converter = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace); LinearRgbToCieXyzConverter converter = GetLinearRgbToCieXyzConverter(color.WorkingSpace);
CieXyz unadapted = converter.Convert(color); CieXyz unadapted = converter.Convert(color);
return this.Adapt(unadapted, color.WorkingSpace.WhitePoint); return this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
@ -454,13 +453,6 @@ public partial class ColorSpaceConverter
/// </summary> /// </summary>
/// <param name="workingSpace">The source working space</param> /// <param name="workingSpace">The source working space</param>
/// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns> /// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns>
private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) private static LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace)
{ => ConverterCache.GetOrAdd(workingSpace, (key) => new LinearRgbToCieXyzConverter(key));
if (this.linearRgbToCieXyzConverter?.SourceWorkingSpace.Equals(workingSpace) == true)
{
return this.linearRgbToCieXyzConverter;
}
return this.linearRgbToCieXyzConverter = new LinearRgbToCieXyzConverter(workingSpace);
}
} }

5
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
@ -12,14 +11,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion;
public partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
// Options. // Options.
private static readonly ColorSpaceConverterOptions DefaultOptions = new ColorSpaceConverterOptions(); private static readonly ColorSpaceConverterOptions DefaultOptions = new();
private readonly Matrix4x4 lmsAdaptationMatrix; private readonly Matrix4x4 lmsAdaptationMatrix;
private readonly CieXyz whitePoint; private readonly CieXyz whitePoint;
private readonly CieXyz targetLuvWhitePoint; private readonly CieXyz targetLuvWhitePoint;
private readonly CieXyz targetLabWhitePoint; private readonly CieXyz targetLabWhitePoint;
private readonly CieXyz targetHunterLabWhitePoint; private readonly CieXyz targetHunterLabWhitePoint;
private readonly RgbWorkingSpace targetRgbWorkingSpace; private readonly RgbWorkingSpace targetRgbWorkingSpace;
private readonly IChromaticAdaptation chromaticAdaptation; private readonly IChromaticAdaptation? chromaticAdaptation;
private readonly bool performChromaticAdaptation; private readonly bool performChromaticAdaptation;
private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter; private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter;
private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter; private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter;

2
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs

@ -44,7 +44,7 @@ public class ColorSpaceConverterOptions
/// <summary> /// <summary>
/// Gets or sets the chromatic adaptation method used. When <value>null</value>, no adaptation will be performed. /// Gets or sets the chromatic adaptation method used. When <value>null</value>, no adaptation will be performed.
/// </summary> /// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation(); public IChromaticAdaptation? ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation();
/// <summary> /// <summary>
/// Gets or sets transformation matrix used in conversion to and from <see cref="Lms"/>. /// Gets or sets transformation matrix used in conversion to and from <see cref="Lms"/>.

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs

@ -75,7 +75,5 @@ public abstract class RgbWorkingSpace
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
return HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
}
} }

9
src/ImageSharp/Common/Helpers/UnitConverter.cs

@ -91,10 +91,13 @@ internal static class UnitConverter
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile) public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile)
{ {
IExifValue<ushort> resolution = profile.GetValue(ExifTag.ResolutionUnit); if (profile.TryGetValue(ExifTag.ResolutionUnit, out IExifValue<ushort>? 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 DefaultResolutionUnit;
return resolution is null ? DefaultResolutionUnit : (PixelResolutionUnit)(byte)(resolution.Value - 1);
} }
/// <summary> /// <summary>

10
src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs

@ -1,23 +1,33 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Compression.Zlib; namespace SixLabors.ImageSharp.Compression.Zlib;
internal static class DeflateThrowHelper internal static class DeflateThrowHelper
{ {
[DoesNotReturn]
public static void ThrowAlreadyFinished() => throw new InvalidOperationException("Finish() already called."); public static void ThrowAlreadyFinished() => throw new InvalidOperationException("Finish() already called.");
[DoesNotReturn]
public static void ThrowAlreadyClosed() => throw new InvalidOperationException("Deflator already closed."); public static void ThrowAlreadyClosed() => throw new InvalidOperationException("Deflator already closed.");
[DoesNotReturn]
public static void ThrowUnknownCompression() => throw new InvalidOperationException("Unknown compression function."); public static void ThrowUnknownCompression() => throw new InvalidOperationException("Unknown compression function.");
[DoesNotReturn]
public static void ThrowNotProcessed() => throw new InvalidOperationException("Old input was not completely processed."); public static void ThrowNotProcessed() => throw new InvalidOperationException("Old input was not completely processed.");
[DoesNotReturn]
public static void ThrowNull(string name) => throw new ArgumentNullException(name); public static void ThrowNull(string name) => throw new ArgumentNullException(name);
[DoesNotReturn]
public static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name); public static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
[DoesNotReturn]
public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated."); public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated.");
[DoesNotReturn]
public static void ThrowNoDeflate() => throw new ImageFormatException("Cannot deflate all input."); public static void ThrowNoDeflate() => throw new ImageFormatException("Cannot deflate all input.");
} }

2
src/ImageSharp/Compression/Zlib/Deflater.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -285,7 +284,6 @@ internal sealed class Deflater : IDisposable
if (!this.isDisposed) if (!this.isDisposed)
{ {
this.engine.Dispose(); this.engine.Dispose();
this.engine = null;
this.isDisposed = true; this.isDisposed = true;
} }
} }

12
src/ImageSharp/Compression/Zlib/DeflaterEngine.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -87,7 +86,7 @@ internal sealed unsafe class DeflaterEngine : IDisposable
/// <summary> /// <summary>
/// The input data for compression. /// The input data for compression.
/// </summary> /// </summary>
private byte[] inputBuf; private byte[]? inputBuf;
/// <summary> /// <summary>
/// The offset into inputBuf, where input data starts. /// The offset into inputBuf, where input data starts.
@ -222,7 +221,7 @@ internal sealed unsafe class DeflaterEngine : IDisposable
/// <param name="buffer">The buffer containing input data.</param> /// <param name="buffer">The buffer containing input data.</param>
/// <param name="offset">The offset of the first byte of data.</param> /// <param name="offset">The offset of the first byte of data.</param>
/// <param name="count">The number of bytes of data to use as input.</param> /// <param name="count">The number of bytes of data to use as input.</param>
public void SetInput(byte[] buffer, int offset, int count) public void SetInput(byte[]? buffer, int offset, int count)
{ {
if (buffer is null) if (buffer is null)
{ {
@ -362,6 +361,8 @@ internal sealed unsafe class DeflaterEngine : IDisposable
more = this.inputEnd - this.inputOff; more = this.inputEnd - this.inputOff;
} }
ArgumentNullException.ThrowIfNull(this.inputBuf);
Unsafe.CopyBlockUnaligned( Unsafe.CopyBlockUnaligned(
ref this.window.Span[this.strstart + this.lookahead], ref this.window.Span[this.strstart + this.lookahead],
ref this.inputBuf[this.inputOff], ref this.inputBuf[this.inputOff],
@ -393,11 +394,6 @@ internal sealed unsafe class DeflaterEngine : IDisposable
this.prevMemoryHandle.Dispose(); this.prevMemoryHandle.Dispose();
this.prevMemoryOwner.Dispose(); this.prevMemoryOwner.Dispose();
this.windowMemoryOwner = null;
this.headMemoryOwner = null;
this.prevMemoryOwner = null;
this.huffman = null;
this.isDisposed = true; this.isDisposed = true;
} }
} }

9
src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -427,10 +426,6 @@ internal sealed unsafe class DeflaterHuffman : IDisposable
this.blTree.Dispose(); this.blTree.Dispose();
this.distTree.Dispose(); this.distTree.Dispose();
this.Pending = null;
this.literalTree = null;
this.blTree = null;
this.distTree = null;
this.isDisposed = true; this.isDisposed = true;
} }
} }
@ -977,10 +972,6 @@ internal sealed unsafe class DeflaterHuffman : IDisposable
this.codesMemoryHandle.Dispose(); this.codesMemoryHandle.Dispose();
this.codesMemoryOwner.Dispose(); this.codesMemoryOwner.Dispose();
this.frequenciesMemoryOwner = null;
this.lengthsMemoryOwner = null;
this.codesMemoryOwner = null;
this.isDisposed = true; this.isDisposed = true;
} }
} }

3
src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -137,8 +136,6 @@ internal sealed class DeflaterOutputStream : Stream
this.memoryOwner.Dispose(); this.memoryOwner.Dispose();
} }
this.deflater = null;
this.memoryOwner = null;
this.isDisposed = true; this.isDisposed = true;
base.Dispose(disposing); base.Dispose(disposing);
} }

2
src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -180,7 +179,6 @@ internal sealed unsafe class DeflaterPendingBuffer : IDisposable
{ {
this.bufferMemoryHandle.Dispose(); this.bufferMemoryHandle.Dispose();
this.bufferMemoryOwner.Dispose(); this.bufferMemoryOwner.Dispose();
this.bufferMemoryOwner = null;
this.isDisposed = true; this.isDisposed = true;
} }
} }

3
src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
@ -172,8 +171,6 @@ internal sealed class ZlibDeflateStream : Stream
this.rawStream.WriteByte((byte)(crc & 0xFF)); this.rawStream.WriteByte((byte)(crc & 0xFF));
} }
this.deflateStream = null;
base.Dispose(disposing); base.Dispose(disposing);
this.isDisposed = true; this.isDisposed = true;
} }

6
src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.IO.Compression; using System.IO.Compression;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
@ -90,7 +90,7 @@ internal sealed class ZlibInflateStream : Stream
/// <summary> /// <summary>
/// Gets the compressed stream over the deframed inner stream. /// Gets the compressed stream over the deframed inner stream.
/// </summary> /// </summary>
public DeflateStream CompressedStream { get; private set; } public DeflateStream? CompressedStream { get; private set; }
/// <summary> /// <summary>
/// Adds new bytes from a frame found in the original stream. /// Adds new bytes from a frame found in the original stream.
@ -98,6 +98,7 @@ internal sealed class ZlibInflateStream : Stream
/// <param name="bytes">The current remaining data according to the chunk length.</param> /// <param name="bytes">The current remaining data according to the chunk length.</param>
/// <param name="isCriticalChunk">Whether the chunk to be inflated is a critical chunk.</param> /// <param name="isCriticalChunk">Whether the chunk to be inflated is a critical chunk.</param>
/// <returns>The <see cref="bool"/>.</returns> /// <returns>The <see cref="bool"/>.</returns>
[MemberNotNullWhen(true, nameof(CompressedStream))]
public bool AllocateNewBytes(int bytes, bool isCriticalChunk) public bool AllocateNewBytes(int bytes, bool isCriticalChunk)
{ {
this.currentDataRemaining = bytes; this.currentDataRemaining = bytes;
@ -210,6 +211,7 @@ internal sealed class ZlibInflateStream : Stream
this.isDisposed = true; this.isDisposed = true;
} }
[MemberNotNullWhen(true, nameof(CompressedStream))]
private bool InitializeInflateStream(bool isCriticalChunk) private bool InitializeInflateStream(bool isCriticalChunk)
{ {
// Read the zlib header : http://tools.ietf.org/html/rfc1950 // Read the zlib header : http://tools.ietf.org/html/rfc1950

7
src/ImageSharp/Diagnostics/MemoryDiagnostics.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
namespace SixLabors.ImageSharp.Diagnostics; namespace SixLabors.ImageSharp.Diagnostics;
@ -17,7 +16,7 @@ public static class MemoryDiagnostics
{ {
private static int totalUndisposedAllocationCount; private static int totalUndisposedAllocationCount;
private static UndisposedAllocationDelegate undisposedAllocation; private static UndisposedAllocationDelegate? undisposedAllocation;
private static int undisposedAllocationSubscriptionCounter; private static int undisposedAllocationSubscriptionCounter;
private static readonly object SyncRoot = new(); private static readonly object SyncRoot = new();
@ -50,12 +49,12 @@ public static class MemoryDiagnostics
/// <summary> /// <summary>
/// Fires when ImageSharp allocates memory from a MemoryAllocator /// Fires when ImageSharp allocates memory from a MemoryAllocator
/// </summary> /// </summary>
internal static event Action MemoryAllocated; internal static event Action? MemoryAllocated;
/// <summary> /// <summary>
/// Fires when ImageSharp releases memory allocated from a MemoryAllocator /// Fires when ImageSharp releases memory allocated from a MemoryAllocator
/// </summary> /// </summary>
internal static event Action MemoryReleased; internal static event Action? MemoryReleased;
/// <summary> /// <summary>
/// Gets a value indicating the total number of memory resource objects leaked to the finalizer. /// Gets a value indicating the total number of memory resource objects leaked to the finalizer.

5
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -171,7 +171,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
quantized = null; quantized = null;
} }
paletteQuantizer.Dispose(); if (hasPaletteQuantizer)
{
paletteQuantizer.Dispose();
}
} }
private void EncodeFrame<TPixel>( private void EncodeFrame<TPixel>(

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -26,16 +26,6 @@ internal readonly struct JFifMarker : IEquatable<JFifMarker>
/// <param name="yDensity">The vertical pixel density.</param> /// <param name="yDensity">The vertical pixel density.</param>
private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity)
{ {
if (xDensity <= 0)
{
JpegThrowHelper.ThrowInvalidImageContentException($"X-Density {xDensity} must be greater than 0.");
}
if (yDensity <= 0)
{
JpegThrowHelper.ThrowInvalidImageContentException($"Y-Density {yDensity} must be greater than 0.");
}
this.MajorVersion = majorVersion; this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion; this.MinorVersion = minorVersion;
@ -64,12 +54,12 @@ internal readonly struct JFifMarker : IEquatable<JFifMarker>
public PixelResolutionUnit DensityUnits { get; } public PixelResolutionUnit DensityUnits { get; }
/// <summary> /// <summary>
/// Gets the horizontal pixel density. Must not be zero. /// Gets the horizontal pixel density.
/// </summary> /// </summary>
public short XDensity { get; } public short XDensity { get; }
/// <summary> /// <summary>
/// Gets the vertical pixel density. Must not be zero. /// Gets the vertical pixel density.
/// </summary> /// </summary>
public short YDensity { get; } public short YDensity { get; }
@ -88,12 +78,8 @@ internal readonly struct JFifMarker : IEquatable<JFifMarker>
byte densityUnits = bytes[7]; byte densityUnits = bytes[7];
short xDensity = (short)((bytes[8] << 8) | bytes[9]); short xDensity = (short)((bytes[8] << 8) | bytes[9]);
short yDensity = (short)((bytes[10] << 8) | bytes[11]); short yDensity = (short)((bytes[10] << 8) | bytes[11]);
marker = new JFifMarker(majorVersion, minorVersion, densityUnits, xDensity, yDensity);
if (xDensity > 0 && yDensity > 0) return true;
{
marker = new JFifMarker(majorVersion, minorVersion, densityUnits, xDensity, yDensity);
return true;
}
} }
marker = default; marker = default;

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs

@ -121,4 +121,10 @@ internal abstract class SpectralConverter
return size; return size;
} }
/// <summary>
/// Gets a value indicating whether the converter has a pixel buffer.
/// </summary>
/// <returns><see langword="true"/> if the converter has a pixel buffer; otherwise, <see langword="false"/>.</returns>
public abstract bool HasPixelBuffer();
} }

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -85,6 +85,12 @@ internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable
/// </summary> /// </summary>
public Configuration Configuration { get; } public Configuration Configuration { get; }
/// <summary>
/// Gets a value indicating whether the converter has a pixel buffer.
/// </summary>
/// <returns><see langword="true"/> if the converter has a pixel buffer; otherwise, <see langword="false"/>.</returns>
public override bool HasPixelBuffer() => this.pixelBuffer is not null;
/// <summary> /// <summary>
/// Gets converted pixel buffer. /// Gets converted pixel buffer.
/// </summary> /// </summary>

19
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -356,6 +356,18 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
// to uint to avoid sign extension. // to uint to avoid sign extension.
if (stream.RemainingBytes < (uint)markerContentByteSize) if (stream.RemainingBytes < (uint)markerContentByteSize)
{ {
if (metadataOnly && this.Metadata != null && this.Frame != null)
{
// We have enough data to decode the image, so we can stop parsing.
return;
}
if (this.Metadata != null && this.Frame != null && spectralConverter.HasPixelBuffer())
{
// We have enough data to decode the image, so we can stop parsing.
return;
}
JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker); JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
} }
@ -705,9 +717,12 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
private double GetExifResolutionValue(ExifTag<Rational> tag) private double GetExifResolutionValue(ExifTag<Rational> tag)
{ {
IExifValue<Rational> resolution = this.Metadata.ExifProfile.GetValue(tag); if (this.Metadata.ExifProfile.TryGetValue(tag, out IExifValue<Rational> resolution))
{
return resolution.Value.ToDouble();
}
return resolution is null ? 0 : resolution.Value.ToDouble(); return 0;
} }
/// <summary> /// <summary>

22
src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs

@ -50,19 +50,21 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor
return left > 0 ? left : 0; return left > 0 ? left : 0;
})) }))
{ {
deframeStream.AllocateNewBytes(byteCount, true); if (deframeStream.AllocateNewBytes(byteCount, true))
DeflateStream dataStream = deframeStream.CompressedStream;
int totalRead = 0;
while (totalRead < buffer.Length)
{ {
int bytesRead = dataStream.Read(buffer, totalRead, buffer.Length - totalRead); DeflateStream? dataStream = deframeStream.CompressedStream;
if (bytesRead <= 0)
int totalRead = 0;
while (totalRead < buffer.Length)
{ {
break; int bytesRead = dataStream.Read(buffer, totalRead, buffer.Length - totalRead);
} if (bytesRead <= 0)
{
break;
}
totalRead += bytesRead; totalRead += bytesRead;
}
} }
} }

31
src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs

@ -67,6 +67,11 @@ internal static class HorizontalPredictor
{ {
ApplyHorizontalPrediction8Bit(rows, width); ApplyHorizontalPrediction8Bit(rows, width);
} }
else if (bitsPerPixel == 16)
{
// Assume rows are L16 grayscale since that's currently the only way 16 bits is supported by encoder
ApplyHorizontalPrediction16Bit(rows, width);
}
else if (bitsPerPixel == 24) else if (bitsPerPixel == 24)
{ {
ApplyHorizontalPrediction24Bit(rows, width); ApplyHorizontalPrediction24Bit(rows, width);
@ -102,6 +107,32 @@ internal static class HorizontalPredictor
} }
} }
/// <summary>
/// Applies a horizontal predictor to the L16 row.
/// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next.
/// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus
/// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly.
/// </summary>
/// <param name="rows">The L16 pixel rows.</param>
/// <param name="width">The width.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static void ApplyHorizontalPrediction16Bit(Span<byte> rows, int width)
{
DebugGuard.IsTrue(rows.Length % width == 0, "Values must be equals");
int height = rows.Length / width;
for (int y = 0; y < height; y++)
{
Span<byte> rowSpan = rows.Slice(y * width, width);
Span<L16> rowL16 = MemoryMarshal.Cast<byte, L16>(rowSpan);
for (int x = rowL16.Length - 1; x >= 1; x--)
{
ushort val = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue);
rowL16[x].PackedValue = val;
}
}
}
/// <summary> /// <summary>
/// Applies a horizontal predictor to a gray pixel row. /// Applies a horizontal predictor to a gray pixel row.
/// </summary> /// </summary>

5
src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs

@ -73,6 +73,11 @@ internal static class TiffConstants
/// </summary> /// </summary>
public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0); public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0);
/// <summary>
/// The bits per sample for 16-bit grayscale images.
/// </summary>
public static readonly TiffBitsPerSample BitsPerSample16Bit = new TiffBitsPerSample(16, 0, 0);
/// <summary> /// <summary>
/// The bits per sample for color images with 8 bits for each color channel. /// The bits per sample for color images with 8 bits for each color channel.
/// </summary> /// </summary>

2
src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs

@ -54,7 +54,7 @@ public enum TiffBitsPerPixel
/// <summary> /// <summary>
/// 16 bits per pixel, for gray images. /// 16 bits per pixel, for gray images.
/// ///
/// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead. /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 16 bits grayscale instead.
/// </summary> /// </summary>
Bit16 = 16, Bit16 = 16,

2
src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs

@ -82,7 +82,7 @@ public readonly struct TiffBitsPerSample : IEquatable<TiffBitsPerSample>
/// <param name="value">The value to parse.</param> /// <param name="value">The value to parse.</param>
/// <param name="sample">The tiff bits per sample.</param> /// <param name="sample">The tiff bits per sample.</param>
/// <returns>True, if the value could be parsed.</returns> /// <returns>True, if the value could be parsed.</returns>
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) if (value is null || value.Length == 0)
{ {

34
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -276,9 +276,15 @@ internal class TiffDecoderCore : IImageDecoderInternals
private void DecodeImageWithStrips<TPixel>(ExifProfile tags, ImageFrame<TPixel> frame, CancellationToken cancellationToken) private void DecodeImageWithStrips<TPixel>(ExifProfile tags, ImageFrame<TPixel> frame, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null int rowsPerStrip;
? (int)tags.GetValue(ExifTag.RowsPerStrip).Value if (tags.TryGetValue(ExifTag.RowsPerStrip, out IExifValue<Number> value))
: TiffConstants.RowsPerStripInfinity; {
rowsPerStrip = (int)value.Value;
}
else
{
rowsPerStrip = TiffConstants.RowsPerStripInfinity;
}
Array stripOffsetsArray = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue(); Array stripOffsetsArray = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue();
Array stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue(); Array stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue();
@ -320,8 +326,18 @@ internal class TiffDecoderCore : IImageDecoderInternals
int width = pixels.Width; int width = pixels.Width;
int height = pixels.Height; int height = pixels.Height;
int tileWidth = (int)tags.GetValue(ExifTag.TileWidth).Value; if (!tags.TryGetValue(ExifTag.TileWidth, out IExifValue<Number> valueWidth))
int tileLength = (int)tags.GetValue(ExifTag.TileLength).Value; {
ArgumentNullException.ThrowIfNull(valueWidth);
}
if (!tags.TryGetValue(ExifTag.TileWidth, out IExifValue<Number> valueLength))
{
ArgumentNullException.ThrowIfNull(valueLength);
}
int tileWidth = (int)valueWidth.Value;
int tileLength = (int)valueLength.Value;
int tilesAcross = (width + tileWidth - 1) / tileWidth; int tilesAcross = (width + tileWidth - 1) / tileWidth;
int tilesDown = (height + tileLength - 1) / tileLength; int tilesDown = (height + tileLength - 1) / tileLength;
@ -775,10 +791,9 @@ internal class TiffDecoderCore : IImageDecoderInternals
/// <returns>The image width.</returns> /// <returns>The image width.</returns>
private static int GetImageWidth(ExifProfile exifProfile) private static int GetImageWidth(ExifProfile exifProfile)
{ {
IExifValue<Number> width = exifProfile.GetValue(ExifTag.ImageWidth); if (!exifProfile.TryGetValue(ExifTag.ImageWidth, out IExifValue<Number> width))
if (width == null)
{ {
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)); DebugGuard.MustBeLessThanOrEqualTo((ulong)width.Value, (ulong)int.MaxValue, nameof(ExifTag.ImageWidth));
@ -793,8 +808,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
/// <returns>The image height.</returns> /// <returns>The image height.</returns>
private static int GetImageHeight(ExifProfile exifProfile) private static int GetImageHeight(ExifProfile exifProfile)
{ {
IExifValue<Number> height = exifProfile.GetValue(ExifTag.ImageLength); if (!exifProfile.TryGetValue(ExifTag.ImageLength, out IExifValue<Number> height))
if (height == null)
{ {
TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageLength"); TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageLength");
} }

22
src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs

@ -38,14 +38,12 @@ internal static class TiffDecoderMetadataCreator
frameMetaData.IptcProfile = new IptcProfile(iptcBytes); frameMetaData.IptcProfile = new IptcProfile(iptcBytes);
} }
IExifValue<byte[]> xmpProfileBytes = frameMetaData.ExifProfile.GetValue(ExifTag.XMP); if (frameMetaData.ExifProfile.TryGetValue(ExifTag.XMP, out IExifValue<byte[]> xmpProfileBytes))
if (xmpProfileBytes != null)
{ {
frameMetaData.XmpProfile = new XmpProfile(xmpProfileBytes.Value); frameMetaData.XmpProfile = new XmpProfile(xmpProfileBytes.Value);
} }
IExifValue<byte[]> iccProfileBytes = frameMetaData.ExifProfile.GetValue(ExifTag.IccProfile); if (frameMetaData.ExifProfile.TryGetValue(ExifTag.IccProfile, out IExifValue<byte[]> iccProfileBytes))
if (iccProfileBytes != null)
{ {
frameMetaData.IccProfile = new IccProfile(iccProfileBytes.Value); frameMetaData.IccProfile = new IccProfile(iccProfileBytes.Value);
} }
@ -70,16 +68,20 @@ internal static class TiffDecoderMetadataCreator
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; 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<Rational> horizontalResolution))
{ {
imageMetaData.HorizontalResolution = horizontalResolution.Value; imageMetaData.HorizontalResolution = horizontalResolution.Value.ToDouble();
} }
double? verticalResolution = exifProfile?.GetValue(ExifTag.YResolution)?.Value.ToDouble(); if (exifProfile.TryGetValue(ExifTag.YResolution, out IExifValue<Rational> verticalResolution))
if (verticalResolution != null)
{ {
imageMetaData.VerticalResolution = verticalResolution.Value; imageMetaData.VerticalResolution = verticalResolution.Value.ToDouble();
} }
} }

94
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<ushort> value))
{
fillOrder = (TiffFillOrder)value.Value;
}
else
{
fillOrder = TiffFillOrder.MostSignificantBitFirst;
}
if (fillOrder == TiffFillOrder.LeastSignificantBitFirst && frameMetadata.BitsPerPixel != TiffBitsPerPixel.Bit1) 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."); 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."); 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; 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]; sampleFormat = sampleFormats[0];
foreach (TiffSampleFormat format in sampleFormats) 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<ushort[]> subSamplingValue))
{
ycbcrSubSampling = subSamplingValue.Value;
}
if (ycbcrSubSampling != null && ycbcrSubSampling.Length != 2) if (ycbcrSubSampling != null && ycbcrSubSampling.Length != 2)
{ {
TiffThrowHelper.ThrowImageFormatException("Invalid YCbCrSubsampling, expected 2 values."); 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."); 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."); TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported.");
} }
options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; if (exifProfile.TryGetValue(ExifTag.PlanarConfiguration, out IExifValue<ushort> planarValue))
{
options.PlanarConfiguration = (TiffPlanarConfiguration)planarValue.Value;
}
else
{
options.PlanarConfiguration = DefaultPlanarConfiguration;
}
options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None; options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None;
options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb; options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb;
options.SampleFormat = sampleFormat ?? TiffSampleFormat.UnsignedInteger; options.SampleFormat = sampleFormat ?? TiffSampleFormat.UnsignedInteger;
options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24;
options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0); options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0);
options.ReferenceBlackAndWhite = exifProfile.GetValue(ExifTag.ReferenceBlackWhite)?.Value;
options.YcbcrCoefficients = exifProfile.GetValue(ExifTag.YCbCrCoefficients)?.Value; if (exifProfile.TryGetValue(ExifTag.ReferenceBlackWhite, out IExifValue<Rational[]> blackWhiteValue))
options.YcbcrSubSampling = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value; {
options.ReferenceBlackAndWhite = blackWhiteValue.Value;
}
if (exifProfile.TryGetValue(ExifTag.YCbCrCoefficients, out IExifValue<Rational[]> coefficientsValue))
{
options.YcbcrCoefficients = coefficientsValue.Value;
}
if (exifProfile.TryGetValue(ExifTag.YCbCrSubsampling, out IExifValue<ushort[]> ycbrSubSamplingValue))
{
options.YcbcrSubSampling = ycbrSubSamplingValue.Value;
}
options.FillOrder = fillOrder; options.FillOrder = fillOrder;
options.JpegTables = exifProfile.GetValue(ExifTag.JPEGTables)?.Value;
options.OldJpegCompressionStartOfImageMarker = exifProfile.GetValue(ExifTag.JPEGInterchangeFormat)?.Value; if (exifProfile.TryGetValue(ExifTag.JPEGTables, out IExifValue<byte[]> jpegTablesValue))
{
options.JpegTables = jpegTablesValue.Value;
}
if (exifProfile.TryGetValue(ExifTag.JPEGInterchangeFormat, out IExifValue<uint> jpegInterchangeFormatValue))
{
options.OldJpegCompressionStartOfImageMarker = jpegInterchangeFormatValue.Value;
}
options.ParseColorType(exifProfile); options.ParseColorType(exifProfile);
options.ParseCompression(frameMetadata.Compression, exifProfile); options.ParseCompression(frameMetadata.Compression, exifProfile);
@ -394,9 +437,9 @@ internal static class TiffDecoderOptionsParser
case TiffPhotometricInterpretation.PaletteColor: case TiffPhotometricInterpretation.PaletteColor:
{ {
options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; if (exifProfile.TryGetValue(ExifTag.ColorMap, out IExifValue<ushort[]> value))
if (options.ColorMap != null)
{ {
options.ColorMap = value.Value;
if (options.BitsPerSample.Channels != 1) if (options.BitsPerSample.Channels != 1)
{ {
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); 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: case TiffPhotometricInterpretation.YCbCr:
{ {
options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; if (exifProfile.TryGetValue(ExifTag.ColorMap, out IExifValue<ushort[]> value))
{
options.ColorMap = value.Value;
}
if (options.BitsPerSample.Channels != 3) if (options.BitsPerSample.Channels != 3)
{ {
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported for YCbCr images."); 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: case TiffCompression.CcittGroup3Fax:
{ {
options.CompressionType = TiffDecoderCompressionType.T4; 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<uint> t4OptionsValue))
{
options.FaxCompressionOptions = (FaxCompressionOptions)t4OptionsValue.Value;
}
else
{
options.FaxCompressionOptions = FaxCompressionOptions.None;
}
break; break;
} }
@ -516,7 +571,14 @@ internal static class TiffDecoderOptionsParser
case TiffCompression.CcittGroup4Fax: case TiffCompression.CcittGroup4Fax:
{ {
options.CompressionType = TiffDecoderCompressionType.T6; 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<uint> t4OptionsValue))
{
options.FaxCompressionOptions = (FaxCompressionOptions)t4OptionsValue.Value;
}
else
{
options.FaxCompressionOptions = FaxCompressionOptions.None;
}
break; break;
} }

22
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -164,8 +164,6 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
TiffNewSubfileType subfileType = (TiffNewSubfileType)(frame.Metadata.ExifProfile?.GetValue(ExifTag.SubfileType)?.Value ?? (int)TiffNewSubfileType.FullImage);
ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker); ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker);
metadataImage = null; metadataImage = null;
} }
@ -378,11 +376,14 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
case TiffBitsPerPixel.Bit8: case TiffBitsPerPixel.Bit8:
this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor); this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break; break;
case TiffBitsPerPixel.Bit16:
// Assume desire to encode as L16 grayscale
this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break;
case TiffBitsPerPixel.Bit6: case TiffBitsPerPixel.Bit6:
case TiffBitsPerPixel.Bit10: case TiffBitsPerPixel.Bit10:
case TiffBitsPerPixel.Bit12: case TiffBitsPerPixel.Bit12:
case TiffBitsPerPixel.Bit14: case TiffBitsPerPixel.Bit14:
case TiffBitsPerPixel.Bit16:
case TiffBitsPerPixel.Bit30: case TiffBitsPerPixel.Bit30:
case TiffBitsPerPixel.Bit36: case TiffBitsPerPixel.Bit36:
case TiffBitsPerPixel.Bit42: case TiffBitsPerPixel.Bit42:
@ -415,13 +416,20 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return; return;
} }
// At the moment only 8 and 32 bits per pixel can be preserved by the tiff encoder. // At the moment only 8, 16 and 32 bits per pixel can be preserved by the tiff encoder.
if (inputBitsPerPixel == 8) if (inputBitsPerPixel == 8)
{ {
this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
return; return;
} }
if (inputBitsPerPixel == 16)
{
// Assume desire to encode as L16 grayscale
this.SetEncoderOptions(TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
return;
}
this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor);
return; return;
} }
@ -436,6 +444,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return; return;
} }
if (inputBitsPerPixel == 16)
{
this.SetEncoderOptions(TiffBitsPerPixel.Bit16, photometricInterpretation, compression, predictor);
return;
}
this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor);
return; return;

20
src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs

@ -343,20 +343,20 @@ internal class TiffEncoderEntriesCollector
return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); return TiffConstants.BitsPerSampleRgb8Bit.ToArray();
case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.WhiteIsZero:
if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) return encoder.BitsPerPixel switch
{ {
return TiffConstants.BitsPerSample1Bit.ToArray(); TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(),
} TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(),
_ => TiffConstants.BitsPerSample8Bit.ToArray()
return TiffConstants.BitsPerSample8Bit.ToArray(); };
case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.BlackIsZero:
if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) return encoder.BitsPerPixel switch
{ {
return TiffConstants.BitsPerSample1Bit.ToArray(); TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(),
} TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(),
_ => TiffConstants.BitsPerSample8Bit.ToArray()
return TiffConstants.BitsPerSample8Bit.ToArray(); };
default: default:
return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); return TiffConstants.BitsPerSampleRgb8Bit.ToArray();

26
src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -78,15 +77,30 @@ public class TiffFrameMetadata : IDeepCloneable
{ {
if (profile != null) if (profile != null)
{ {
if (TiffBitsPerSample.TryParse(profile.GetValue(ExifTag.BitsPerSample)?.Value, out TiffBitsPerSample bitsPerSample)) if (profile.TryGetValue(ExifTag.BitsPerSample, out IExifValue<ushort[]>? bitsPerSampleValue))
{ {
meta.BitsPerSample = bitsPerSample; if (TiffBitsPerSample.TryParse(bitsPerSampleValue.Value, out TiffBitsPerSample bitsPerSample))
{
meta.BitsPerSample = bitsPerSample;
}
} }
meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel();
meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value;
meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; if (profile.TryGetValue(ExifTag.Compression, out IExifValue<ushort>? compressionValue))
meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; {
meta.Compression = (TiffCompression)compressionValue.Value;
}
if (profile.TryGetValue(ExifTag.PhotometricInterpretation, out IExifValue<ushort>? photometricInterpretationValue))
{
meta.PhotometricInterpretation = (TiffPhotometricInterpretation)photometricInterpretationValue.Value;
}
if (profile.TryGetValue(ExifTag.Predictor, out IExifValue<ushort>? predictorValue))
{
meta.Predictor = (TiffPredictor)predictorValue.Value;
}
profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.BitsPerSample);
profile.RemoveValue(ExifTag.Compression); profile.RemoveValue(ExifTag.Compression);

12
src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs

@ -1,21 +1,33 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Formats.Tiff; namespace SixLabors.ImageSharp.Formats.Tiff;
internal static class TiffThrowHelper internal static class TiffThrowHelper
{ {
[DoesNotReturn]
public static Exception ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); 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}"); 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}"); 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}"); 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."); public static Exception ThrowInvalidHeader() => throw new ImageFormatException("Invalid TIFF file header.");
[DoesNotReturn]
public static void ThrowNotSupported(string message) => throw new NotSupportedException(message); public static void ThrowNotSupported(string message) => throw new NotSupportedException(message);
[DoesNotReturn]
public static void ThrowArgumentException(string message) => throw new ArgumentException(message); public static void ThrowArgumentException(string message) => throw new ArgumentException(message);
} }

9
src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs

@ -27,12 +27,13 @@ internal static class TiffColorWriterFactory
return new TiffPaletteWriter<TPixel>(image, quantizer, pixelSamplingStrategy, memoryAllocator, configuration, entriesCollector, bitsPerPixel); return new TiffPaletteWriter<TPixel>(image, quantizer, pixelSamplingStrategy, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.WhiteIsZero:
if (bitsPerPixel == 1) return bitsPerPixel switch
{ {
return new TiffBiColorWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector); 1 => new TiffBiColorWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector),
} 16 => new TiffGrayL16Writer<TPixel>(image, memoryAllocator, configuration, entriesCollector),
_ => new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector)
};
return new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
default: default:
return new TiffRgbWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector); return new TiffRgbWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
} }

22
src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Writers;
internal sealed class TiffGrayL16Writer<TPixel> : TiffCompositeColorWriter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
public TiffGrayL16Writer(ImageFrame<TPixel> image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector)
: base(image, memoryAllocator, configuration, entriesCollector)
{
}
/// <inheritdoc />
public override int BitsPerPixel => 16;
/// <inheritdoc />
protected override void EncodePixels(Span<TPixel> pixels, Span<byte> buffer) => PixelOperations<TPixel>.Instance.ToL16Bytes(this.Configuration, pixels, buffer, pixels.Length);
}

11
src/ImageSharp/Memory/Allocators/Internals/Gen2GcCallback.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
// Port of BCL internal utility: // Port of BCL internal utility:
// https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/System.Private.CoreLib/src/System/Gen2GcCallback.cs // https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/System.Private.CoreLib/src/System/Gen2GcCallback.cs
@ -15,8 +14,8 @@ namespace SixLabors.ImageSharp.Memory.Internals;
/// </summary> /// </summary>
internal sealed class Gen2GcCallback : CriticalFinalizerObject internal sealed class Gen2GcCallback : CriticalFinalizerObject
{ {
private readonly Func<bool> callback0; private readonly Func<bool>? callback0;
private readonly Func<object, bool> callback1; private readonly Func<object, bool>? callback1;
private GCHandle weakTargetObj; private GCHandle weakTargetObj;
private Gen2GcCallback(Func<bool> callback) => this.callback0 = callback; private Gen2GcCallback(Func<bool> callback) => this.callback0 = callback;
@ -32,7 +31,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject
if (this.weakTargetObj.IsAllocated) if (this.weakTargetObj.IsAllocated)
{ {
// Check to see if the target object is still alive. // Check to see if the target object is still alive.
object targetObj = this.weakTargetObj.Target; object? targetObj = this.weakTargetObj.Target;
if (targetObj == null) if (targetObj == null)
{ {
// The target object is dead, so this callback object is no longer needed. // The target object is dead, so this callback object is no longer needed.
@ -43,7 +42,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject
// Execute the callback method. // Execute the callback method.
try try
{ {
if (!this.callback1(targetObj)) if (!this.callback1!(targetObj))
{ {
// If the callback returns false, this callback object is no longer needed. // If the callback returns false, this callback object is no longer needed.
this.weakTargetObj.Free(); this.weakTargetObj.Free();
@ -64,7 +63,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject
// Execute the callback method. // Execute the callback method.
try try
{ {
if (!this.callback0()) if (!this.callback0!())
{ {
// If the callback returns false, this callback object is no longer needed. // If the callback returns false, this callback object is no longer needed.
return; return;

3
src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.Diagnostics; using SixLabors.ImageSharp.Diagnostics;
@ -15,7 +14,7 @@ internal abstract class RefCountedMemoryLifetimeGuard : IDisposable
private int refCount = 1; private int refCount = 1;
private int disposed; private int disposed;
private int released; private int released;
private string allocationStackTrace; private string? allocationStackTrace;
protected RefCountedMemoryLifetimeGuard() protected RefCountedMemoryLifetimeGuard()
{ {

15
src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -22,7 +22,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
this.lifetimeGuard = new LifetimeGuard(this.Array); this.lifetimeGuard = new LifetimeGuard(this.Array);
} }
public byte[] Array { get; private set; } public byte[]? Array { get; private set; }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
@ -41,7 +41,11 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
return MemoryMarshal.Cast<byte, T>(this.Array.AsSpan(0, this.lengthInBytes)); return MemoryMarshal.Cast<byte, T>(this.Array.AsSpan(0, this.lengthInBytes));
} }
protected override object GetPinnableObject() => this.Array; protected override object GetPinnableObject()
{
this.CheckDisposed();
return this.Array;
}
public void AddRef() public void AddRef()
{ {
@ -52,6 +56,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
public void ReleaseRef() => this.lifetimeGuard.ReleaseRef(); public void ReleaseRef() => this.lifetimeGuard.ReleaseRef();
[Conditional("DEBUG")] [Conditional("DEBUG")]
[MemberNotNull(nameof(Array))]
private void CheckDisposed() private void CheckDisposed()
{ {
if (this.Array == null) if (this.Array == null)
@ -62,7 +67,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard
{ {
private byte[] array; private byte[]? array;
public LifetimeGuard(byte[] array) => this.array = array; public LifetimeGuard(byte[] array) => this.array = array;
@ -73,7 +78,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
// This is not ideal, but subsequent leaks will end up returning arrays to per-cpu buckets, // This is not ideal, but subsequent leaks will end up returning arrays to per-cpu buckets,
// meaning likely a different bucket than it was rented from, // meaning likely a different bucket than it was rented from,
// but this is PROBABLY better than not returning the arrays at all. // but this is PROBABLY better than not returning the arrays at all.
ArrayPool<byte>.Shared.Return(this.array); ArrayPool<byte>.Shared.Return(this.array!);
this.array = null; this.array = null;
} }
} }

7
src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics; using System.Diagnostics;
@ -13,7 +12,7 @@ internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedEx
{ {
private static int minTrimPeriodMilliseconds = int.MaxValue; private static int minTrimPeriodMilliseconds = int.MaxValue;
private static readonly List<WeakReference<UniformUnmanagedMemoryPool>> AllPools = new(); private static readonly List<WeakReference<UniformUnmanagedMemoryPool>> AllPools = new();
private static Timer trimTimer; private static Timer? trimTimer;
private static readonly Stopwatch Stopwatch = Stopwatch.StartNew(); private static readonly Stopwatch Stopwatch = Stopwatch.StartNew();
@ -97,7 +96,7 @@ internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedEx
/// <summary> /// <summary>
/// Rent <paramref name="bufferCount"/> buffers or return 'null' if the pool is full. /// Rent <paramref name="bufferCount"/> buffers or return 'null' if the pool is full.
/// </summary> /// </summary>
public UnmanagedMemoryHandle[] Rent(int bufferCount) public UnmanagedMemoryHandle[]? Rent(int bufferCount)
{ {
UnmanagedMemoryHandle[] buffersLocal = this.buffers; UnmanagedMemoryHandle[] buffersLocal = this.buffers;
@ -248,7 +247,7 @@ internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedEx
foreach (WeakReference<UniformUnmanagedMemoryPool> weakPoolRef in AllPools) foreach (WeakReference<UniformUnmanagedMemoryPool> weakPoolRef in AllPools)
{ {
if (weakPoolRef.TryGetTarget(out UniformUnmanagedMemoryPool pool)) if (weakPoolRef.TryGetTarget(out UniformUnmanagedMemoryPool? pool))
{ {
pool.Trim(); pool.Trim();
} }

11
src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -20,7 +19,7 @@ internal struct UnmanagedMemoryHandle : IEquatable<UnmanagedMemoryHandle>
private static long totalOomRetries; private static long totalOomRetries;
// A Monitor to wait/signal when we are low on memory. // A Monitor to wait/signal when we are low on memory.
private static object lowMemoryMonitor; private static object? lowMemoryMonitor;
public static readonly UnmanagedMemoryHandle NullHandle; public static readonly UnmanagedMemoryHandle NullHandle;
@ -114,9 +113,9 @@ internal struct UnmanagedMemoryHandle : IEquatable<UnmanagedMemoryHandle>
if (Volatile.Read(ref lowMemoryMonitor) != null) if (Volatile.Read(ref lowMemoryMonitor) != null)
{ {
// We are low on memory. Signal all threads waiting in AllocateHandle(). // We are low on memory. Signal all threads waiting in AllocateHandle().
Monitor.Enter(lowMemoryMonitor); Monitor.Enter(lowMemoryMonitor!);
Monitor.PulseAll(lowMemoryMonitor); Monitor.PulseAll(lowMemoryMonitor!);
Monitor.Exit(lowMemoryMonitor); Monitor.Exit(lowMemoryMonitor!);
} }
this.lengthInBytes = 0; this.lengthInBytes = 0;
@ -124,7 +123,7 @@ internal struct UnmanagedMemoryHandle : IEquatable<UnmanagedMemoryHandle>
public bool Equals(UnmanagedMemoryHandle other) => this.handle.Equals(other.handle); public bool Equals(UnmanagedMemoryHandle other) => this.handle.Equals(other.handle);
public override bool Equals(object obj) => obj is UnmanagedMemoryHandle other && this.Equals(other); public override bool Equals(object? obj) => obj is UnmanagedMemoryHandle other && this.Equals(other);
public override int GetHashCode() => this.handle.GetHashCode(); public override int GetHashCode() => this.handle.GetHashCode();
} }

2
src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs

@ -135,7 +135,7 @@ internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocato
} }
// Attempt to rent the whole group from the pool, allocate a group of unmanaged buffers if the attempt fails: // Attempt to rent the whole group from the pool, allocate a group of unmanaged buffers if the attempt fails:
if (MemoryGroup<T>.TryAllocate(this.pool, totalLength, bufferAlignment, options, out MemoryGroup<T> poolGroup)) if (MemoryGroup<T>.TryAllocate(this.pool, totalLength, bufferAlignment, options, out MemoryGroup<T>? poolGroup))
{ {
return poolGroup; return poolGroup;
} }

2
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs

@ -106,7 +106,7 @@ internal static class MemoryGroupExtensions
} }
} }
internal static void CopyTo<T>(this IMemoryGroup<T> source, IMemoryGroup<T> target) internal static void CopyTo<T>(this IMemoryGroup<T>? source, IMemoryGroup<T>? target)
where T : struct where T : struct
{ {
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));

2
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Memory;
internal unsafe struct MemoryGroupSpanCache internal unsafe struct MemoryGroupSpanCache
{ {
public SpanCacheMode Mode; public SpanCacheMode Mode;
public byte[] SingleArray; public byte[]? SingleArray;
public void* SinglePointer; public void* SinglePointer;
public void*[] MultiPointer; public void*[] MultiPointer;

6
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Collections; using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Memory;
@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory;
internal class MemoryGroupView<T> : IMemoryGroup<T> internal class MemoryGroupView<T> : IMemoryGroup<T>
where T : struct where T : struct
{ {
private MemoryGroup<T> owner; private MemoryGroup<T>? owner;
private readonly MemoryOwnerWrapper[] memoryWrappers; private readonly MemoryOwnerWrapper[] memoryWrappers;
public MemoryGroupView(MemoryGroup<T> owner) public MemoryGroupView(MemoryGroup<T> owner)
@ -63,6 +63,7 @@ internal class MemoryGroupView<T> : IMemoryGroup<T>
} }
} }
[MemberNotNullWhen(true, nameof(owner))]
public bool IsValid => this.owner != null; public bool IsValid => this.owner != null;
public Memory<T> this[int index] public Memory<T> this[int index]
@ -99,6 +100,7 @@ internal class MemoryGroupView<T> : IMemoryGroup<T>
this.owner = null; this.owner = null;
} }
[MemberNotNull(nameof(owner))]
private void EnsureIsValid() private void EnsureIsValid()
{ {
if (!this.IsValid) if (!this.IsValid)

10
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs

@ -1,8 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory.Internals; using SixLabors.ImageSharp.Memory.Internals;
@ -15,8 +15,8 @@ internal abstract partial class MemoryGroup<T>
/// </summary> /// </summary>
public sealed class Owned : MemoryGroup<T>, IEnumerable<Memory<T>> public sealed class Owned : MemoryGroup<T>, IEnumerable<Memory<T>>
{ {
private IMemoryOwner<T>[] memoryOwners; private IMemoryOwner<T>[]? memoryOwners;
private RefCountedMemoryLifetimeGuard groupLifetimeGuard; private RefCountedMemoryLifetimeGuard? groupLifetimeGuard;
public Owned(IMemoryOwner<T>[] memoryOwners, int bufferLength, long totalLength, bool swappable) public Owned(IMemoryOwner<T>[] memoryOwners, int bufferLength, long totalLength, bool swappable)
: base(bufferLength, totalLength) : base(bufferLength, totalLength)
@ -149,7 +149,7 @@ internal abstract partial class MemoryGroup<T>
} }
else else
{ {
foreach (IMemoryOwner<T> memoryOwner in this.memoryOwners) foreach (IMemoryOwner<T> memoryOwner in this.memoryOwners!)
{ {
memoryOwner.Dispose(); memoryOwner.Dispose();
} }
@ -161,6 +161,7 @@ internal abstract partial class MemoryGroup<T>
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
[MemberNotNull(nameof(memoryOwners))]
private void EnsureNotDisposed() private void EnsureNotDisposed()
{ {
if (this.memoryOwners is null) if (this.memoryOwners is null)
@ -170,6 +171,7 @@ internal abstract partial class MemoryGroup<T>
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
[DoesNotReturn]
private static void ThrowObjectDisposedException() => throw new ObjectDisposedException(nameof(MemoryGroup<T>)); private static void ThrowObjectDisposedException() => throw new ObjectDisposedException(nameof(MemoryGroup<T>));
// When the MemoryGroup points to multiple buffers via `groupLifetimeGuard`, // When the MemoryGroup points to multiple buffers via `groupLifetimeGuard`,

8
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Collections; using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory.Internals; using SixLabors.ImageSharp.Memory.Internals;
@ -41,7 +41,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
/// <inheritdoc /> /// <inheritdoc />
public bool IsValid { get; private set; } = true; public bool IsValid { get; private set; } = true;
public MemoryGroupView<T> View { get; private set; } public MemoryGroupView<T> View { get; private set; } = null!;
/// <inheritdoc /> /// <inheritdoc />
public abstract Memory<T> this[int index] { get; } public abstract Memory<T> this[int index] { get; }
@ -150,7 +150,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
long totalLengthInElements, long totalLengthInElements,
int bufferAlignmentInElements, int bufferAlignmentInElements,
AllocationOptions options, AllocationOptions options,
out MemoryGroup<T> memoryGroup) [NotNullWhen(true)] out MemoryGroup<T>? memoryGroup)
{ {
Guard.NotNull(pool, nameof(pool)); Guard.NotNull(pool, nameof(pool));
Guard.MustBeGreaterThanOrEqualTo(totalLengthInElements, 0, nameof(totalLengthInElements)); Guard.MustBeGreaterThanOrEqualTo(totalLengthInElements, 0, nameof(totalLengthInElements));
@ -188,7 +188,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
bufferCount++; bufferCount++;
} }
UnmanagedMemoryHandle[] arrays = pool.Rent(bufferCount); UnmanagedMemoryHandle[]? arrays = pool.Rent(bufferCount);
if (arrays == null) if (arrays == null)
{ {

10
src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text; using System.Text;
@ -27,7 +26,14 @@ internal static class ExifEncodedStringHelpers
// 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990) // 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 // 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
{
get
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
return Encoding.GetEncoding(20932);
}
}
public static bool IsEncodedString(ExifTagValue tag) => tag switch public static bool IsEncodedString(ExifTagValue tag) => tag switch
{ {

89
src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -14,12 +14,12 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// <summary> /// <summary>
/// The byte array to read the EXIF profile from. /// The byte array to read the EXIF profile from.
/// </summary> /// </summary>
private readonly byte[] data; private readonly byte[]? data;
/// <summary> /// <summary>
/// The collection of EXIF values /// The collection of EXIF values
/// </summary> /// </summary>
private List<IExifValue> values; private List<IExifValue>? values;
/// <summary> /// <summary>
/// The thumbnail offset position in the byte stream /// The thumbnail offset position in the byte stream
@ -35,7 +35,7 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// Initializes a new instance of the <see cref="ExifProfile"/> class. /// Initializes a new instance of the <see cref="ExifProfile"/> class.
/// </summary> /// </summary>
public ExifProfile() public ExifProfile()
: this((byte[])null) : this((byte[]?)null)
{ {
} }
@ -43,7 +43,7 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// Initializes a new instance of the <see cref="ExifProfile"/> class. /// Initializes a new instance of the <see cref="ExifProfile"/> class.
/// </summary> /// </summary>
/// <param name="data">The byte array to read the EXIF profile from.</param> /// <param name="data">The byte array to read the EXIF profile from.</param>
public ExifProfile(byte[] data) public ExifProfile(byte[]? data)
{ {
this.Parts = ExifParts.All; this.Parts = ExifParts.All;
this.data = data; this.data = data;
@ -78,7 +78,7 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
this.InvalidTags = other.InvalidTags.Count > 0 this.InvalidTags = other.InvalidTags.Count > 0
? new List<ExifTag>(other.InvalidTags) ? new List<ExifTag>(other.InvalidTags)
: (IReadOnlyList<ExifTag>)Array.Empty<ExifTag>(); : Array.Empty<ExifTag>();
if (other.values != null) if (other.values != null)
{ {
@ -110,6 +110,7 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// <summary> /// <summary>
/// Gets the values of this EXIF profile. /// Gets the values of this EXIF profile.
/// </summary> /// </summary>
[MemberNotNull(nameof(values))]
public IReadOnlyList<IExifValue> Values public IReadOnlyList<IExifValue> Values
{ {
get get
@ -122,36 +123,47 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// <summary> /// <summary>
/// Returns the thumbnail in the EXIF profile when available. /// Returns the thumbnail in the EXIF profile when available.
/// </summary> /// </summary>
/// <param name="image">The thumbnail</param>
/// <returns> /// <returns>
/// The <see cref="Image"/>. /// True, if there is a thumbnail otherwise false.
/// </returns> /// </returns>
public Image CreateThumbnail() => this.CreateThumbnail<Rgba32>(); public bool TryCreateThumbnail([NotNullWhen(true)] out Image? image)
{
if (this.TryCreateThumbnail(out Image<Rgba32>? innerimage))
{
image = innerimage;
return true;
}
image = null;
return false;
}
/// <summary> /// <summary>
/// Returns the thumbnail in the EXIF profile when available. /// Returns the thumbnail in the EXIF profile when available.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns> /// <param name="image">The thumbnail.</param>
/// The <see cref="Image{TPixel}"/>. /// <returns>True, if there is a thumbnail otherwise false.</returns>
/// </returns> public bool TryCreateThumbnail<TPixel>([NotNullWhen(true)] out Image<TPixel>? image)
public Image<TPixel> CreateThumbnail<TPixel>()
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
this.InitializeValues(); this.InitializeValues();
image = null;
if (this.thumbnailOffset == 0 || this.thumbnailLength == 0) if (this.thumbnailOffset == 0 || this.thumbnailLength == 0)
{ {
return null; return false;
} }
if (this.data is null || this.data.Length < (this.thumbnailOffset + this.thumbnailLength)) 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)) using (MemoryStream memStream = new(this.data, this.thumbnailOffset, this.thumbnailLength))
{ {
return Image.Load<TPixel>(memStream); image = Image.Load<TPixel>(memStream);
return true;
} }
} }
@ -159,12 +171,21 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// Returns the value with the specified tag. /// Returns the value with the specified tag.
/// </summary> /// </summary>
/// <param name="tag">The tag of the exif value.</param> /// <param name="tag">The tag of the exif value.</param>
/// <returns>The value with the specified tag.</returns> /// <param name="exifValue">The value with the specified tag.</param>
/// <returns>True when found, otherwise false</returns>
/// <typeparam name="TValueType">The data type of the tag.</typeparam> /// <typeparam name="TValueType">The data type of the tag.</typeparam>
public IExifValue<TValueType> GetValue<TValueType>(ExifTag<TValueType> tag) public bool TryGetValue<TValueType>(ExifTag<TValueType> tag, [NotNullWhen(true)] out IExifValue<TValueType>? exifValue)
{ {
IExifValue value = this.GetValueInternal(tag); IExifValue? value = this.GetValueInternal(tag);
return value is null ? null : (IExifValue<TValueType>)value;
if (value is null)
{
exifValue = null;
return false;
}
exifValue = (IExifValue<TValueType>)value;
return true;
} }
/// <summary> /// <summary>
@ -203,7 +224,7 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// Converts this instance to a byte array. /// Converts this instance to a byte array.
/// </summary> /// </summary>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray() public byte[]? ToByteArray()
{ {
if (this.values is null) if (this.values is null)
{ {
@ -215,19 +236,19 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
return Array.Empty<byte>(); return Array.Empty<byte>();
} }
var writer = new ExifWriter(this.values, this.Parts); ExifWriter writer = new(this.values, this.Parts);
return writer.GetData(); return writer.GetData();
} }
/// <inheritdoc/> /// <inheritdoc/>
public ExifProfile DeepClone() => new ExifProfile(this); public ExifProfile DeepClone() => new(this);
/// <summary> /// <summary>
/// Returns the value with the specified tag. /// Returns the value with the specified tag.
/// </summary> /// </summary>
/// <param name="tag">The tag of the exif value.</param> /// <param name="tag">The tag of the exif value.</param>
/// <returns>The value with the specified tag.</returns> /// <returns>The value with the specified tag.</returns>
internal IExifValue GetValueInternal(ExifTag tag) internal IExifValue? GetValueInternal(ExifTag tag)
{ {
foreach (IExifValue exifValue in this.Values) foreach (IExifValue exifValue in this.Values)
{ {
@ -245,7 +266,8 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
/// </summary> /// </summary>
/// <param name="tag">The tag of the exif value.</param> /// <param name="tag">The tag of the exif value.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
internal void SetValueInternal(ExifTag tag, object value) /// <exception cref="NotSupportedException">Newly created value is null.</exception>
internal void SetValueInternal(ExifTag tag, object? value)
{ {
foreach (IExifValue exifValue in this.Values) foreach (IExifValue exifValue in this.Values)
{ {
@ -256,10 +278,10 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
} }
} }
ExifValue newExifValue = ExifValues.Create(tag); ExifValue? newExifValue = ExifValues.Create(tag);
if (newExifValue is null) if (newExifValue is null)
{ {
throw new NotSupportedException(); throw new NotSupportedException($"Newly created value for tag {tag} is null.");
} }
newExifValue.TrySetValue(value); newExifValue.TrySetValue(value);
@ -278,9 +300,7 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
private void SyncResolution(ExifTag<Rational> tag, double resolution) private void SyncResolution(ExifTag<Rational> tag, double resolution)
{ {
IExifValue<Rational> value = this.GetValue(tag); if (!this.TryGetValue(tag, out IExifValue<Rational>? value))
if (value is null)
{ {
return; return;
} }
@ -290,10 +310,11 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
this.RemoveValue(value.Tag); this.RemoveValue(value.Tag);
} }
var newResolution = new Rational(resolution, false); Rational newResolution = new(resolution, false);
this.SetValue(tag, newResolution); this.SetValue(tag, newResolution);
} }
[MemberNotNull(nameof(values))]
private void InitializeValues() private void InitializeValues()
{ {
if (this.values != null) if (this.values != null)
@ -307,13 +328,13 @@ public sealed class ExifProfile : IDeepCloneable<ExifProfile>
return; return;
} }
var reader = new ExifReader(this.data); ExifReader reader = new(this.data);
this.values = reader.ReadValues(); this.values = reader.ReadValues();
this.InvalidTags = reader.InvalidTags.Count > 0 this.InvalidTags = reader.InvalidTags.Count > 0
? new List<ExifTag>(reader.InvalidTags) ? new List<ExifTag>(reader.InvalidTags)
: (IReadOnlyList<ExifTag>)Array.Empty<ExifTag>(); : Array.Empty<ExifTag>();
this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailOffset = (int)reader.ThumbnailOffset;
this.thumbnailLength = (int)reader.ThumbnailLength; this.thumbnailLength = (int)reader.ThumbnailLength;

28
src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
@ -20,9 +19,10 @@ 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) : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData))), allocator)
{ {
// TODO: We never call this constructor passing a non-null allocator.
} }
/// <summary> /// <summary>
@ -90,13 +90,13 @@ internal abstract class BaseExifReader
private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf4 = new byte[4];
private readonly byte[] buf2 = new byte[2]; private readonly byte[] buf2 = new byte[2];
private readonly MemoryAllocator allocator; private readonly MemoryAllocator? allocator;
private readonly Stream data; private readonly Stream data;
private List<ExifTag> invalidTags; private List<ExifTag>? invalidTags;
private List<ulong> subIfds; private List<ulong>? subIfds;
protected BaseExifReader(Stream stream, MemoryAllocator allocator) protected BaseExifReader(Stream stream, MemoryAllocator? allocator)
{ {
this.data = stream ?? throw new ArgumentNullException(nameof(stream)); this.data = stream ?? throw new ArgumentNullException(nameof(stream));
this.allocator = allocator; this.allocator = allocator;
@ -219,7 +219,7 @@ internal abstract class BaseExifReader
this.Seek(tag.Offset); this.Seek(tag.Offset);
if (this.TryReadSpan(buffer)) 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); this.Add(values, tag.Exif, value);
} }
} }
@ -255,7 +255,7 @@ internal abstract class BaseExifReader
private static byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0]; private static byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private object ConvertValue(ExifDataType dataType, ReadOnlySpan<byte> buffer, bool isArray) private object? ConvertValue(ExifDataType dataType, ReadOnlySpan<byte> buffer, bool isArray)
{ {
if (buffer.Length == 0) if (buffer.Length == 0)
{ {
@ -390,7 +390,7 @@ internal abstract class BaseExifReader
numberOfComponents = 4 / ExifDataTypes.GetSize(dataType); 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) if (exifValue is null)
{ {
@ -414,7 +414,7 @@ internal abstract class BaseExifReader
} }
else 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); this.Add(values, exifValue, value);
} }
} }
@ -443,7 +443,7 @@ internal abstract class BaseExifReader
numberOfComponents = 8 / ExifDataTypes.GetSize(dataType); numberOfComponents = 8 / ExifDataTypes.GetSize(dataType);
} }
ExifValue exifValue = tag switch ExifValue? exifValue = tag switch
{ {
ExifTagValue.StripOffsets => new ExifLong8Array(ExifTagValue.StripOffsets), ExifTagValue.StripOffsets => new ExifLong8Array(ExifTagValue.StripOffsets),
ExifTagValue.StripByteCounts => new ExifLong8Array(ExifTagValue.StripByteCounts), ExifTagValue.StripByteCounts => new ExifLong8Array(ExifTagValue.StripByteCounts),
@ -471,12 +471,12 @@ internal abstract class BaseExifReader
} }
else 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); this.Add(values, exifValue, value);
} }
} }
private void Add(IList<IExifValue> values, IExifValue exif, object value) private void Add(IList<IExifValue> values, IExifValue exif, object? value)
{ {
if (!exif.TrySetValue(value)) if (!exif.TrySetValue(value))
{ {
@ -510,7 +510,7 @@ internal abstract class BaseExifReader
private void AddInvalidTag(ExifTag tag) private void AddInvalidTag(ExifTag tag)
=> (this.invalidTags ??= new List<ExifTag>()).Add(tag); => (this.invalidTags ??= new List<ExifTag>()).Add(tag);
private void AddSubIfd(object val) private void AddSubIfd(object? val)
=> (this.subIfds ??= new List<ulong>()).Add(Convert.ToUInt64(val, CultureInfo.InvariantCulture)); => (this.subIfds ??= new List<ulong>()).Add(Convert.ToUInt64(val, CultureInfo.InvariantCulture));
private void Seek(ulong pos) private void Seek(ulong pos)

23
src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -26,29 +26,34 @@ internal sealed class ExifTagDescriptionAttribute : Attribute
/// </summary> /// </summary>
/// <param name="tag">The tag.</param> /// <param name="tag">The tag.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="description">The description.</param>
/// <returns> /// <returns>
/// The <see cref="string"/>. /// True when description was found
/// </returns> /// </returns>
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); FieldInfo? field = typeof(ExifTagValue).GetField(tagValue.ToString(), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
description = null;
if (field is null) if (field is null)
{ {
return null; return false;
} }
foreach (CustomAttributeData customAttribute in field.CustomAttributes) foreach (CustomAttributeData customAttribute in field.CustomAttributes)
{ {
object attributeValue = customAttribute.ConstructorArguments[0].Value; object? attributeValue = customAttribute.ConstructorArguments[0].Value;
if (Equals(attributeValue, value)) 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;
} }
} }

37
src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers.Binary; using System.Buffers.Binary;
@ -16,7 +15,7 @@ internal sealed class ExifWriter
/// </summary> /// </summary>
private readonly ExifParts allowedParts; private readonly ExifParts allowedParts;
private readonly IList<IExifValue> values; private readonly IList<IExifValue> values;
private List<int> dataOffsets; private List<int>? dataOffsets;
private readonly List<IExifValue> ifdValues; private readonly List<IExifValue> ifdValues;
private readonly List<IExifValue> exifValues; private readonly List<IExifValue> exifValues;
private readonly List<IExifValue> gpsValues; private readonly List<IExifValue> gpsValues;
@ -45,8 +44,8 @@ internal sealed class ExifWriter
{ {
const uint startIndex = 0; const uint startIndex = 0;
IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset); IExifValue? exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset);
IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset); IExifValue? gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset);
uint ifdLength = GetLength(this.ifdValues); uint ifdLength = GetLength(this.ifdValues);
uint exifLength = GetLength(this.exifValues); uint exifLength = GetLength(this.exifValues);
@ -91,7 +90,7 @@ internal sealed class ExifWriter
if (gpsLength > 0) if (gpsLength > 0)
{ {
i = this.WriteHeaders(this.gpsValues, result, i); i = this.WriteHeaders(this.gpsValues, result, i);
i = this.WriteData(startIndex, this.gpsValues, result, i); this.WriteData(startIndex, this.gpsValues, result, i);
} }
return result; return result;
@ -160,7 +159,7 @@ internal sealed class ExifWriter
return offset + 4; return offset + 4;
} }
private static IExifValue GetOffsetValue(List<IExifValue> ifdValues, List<IExifValue> values, ExifTag offset) private static IExifValue? GetOffsetValue(List<IExifValue> ifdValues, List<IExifValue> values, ExifTag offset)
{ {
int index = -1; int index = -1;
@ -179,8 +178,12 @@ internal sealed class ExifWriter
return ifdValues[index]; return ifdValues[index];
} }
ExifValue result = ExifValues.Create(offset); ExifValue? result = ExifValues.Create(offset);
ifdValues.Add(result);
if (result is not null)
{
ifdValues.Add(result);
}
return result; return result;
} }
@ -219,15 +222,14 @@ internal sealed class ExifWriter
private static bool HasValue(IExifValue exifValue) private static bool HasValue(IExifValue exifValue)
{ {
object value = exifValue.GetValue(); object? value = exifValue.GetValue();
if (value is null) if (value is null)
{ {
return false; return false;
} }
if (exifValue.DataType == ExifDataType.Ascii) if (exifValue.DataType == ExifDataType.Ascii && value is string stringValue)
{ {
string stringValue = (string)value;
return stringValue.Length > 0; return stringValue.Length > 0;
} }
@ -270,11 +272,11 @@ internal sealed class ExifWriter
internal static uint GetNumberOfComponents(IExifValue exifValue) internal static uint GetNumberOfComponents(IExifValue exifValue)
{ {
object value = exifValue.GetValue(); object? value = exifValue.GetValue();
if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) 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) if (value is EncodedString encodedString)
@ -284,7 +286,7 @@ internal sealed class ExifWriter
if (exifValue.DataType == ExifDataType.Ascii) 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) if (value is Array arrayValue)
@ -298,7 +300,7 @@ internal sealed class ExifWriter
private static int WriteArray(IExifValue value, Span<byte> destination, int offset) private static int WriteArray(IExifValue value, Span<byte> destination, int offset)
{ {
int newOffset = 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); newOffset = WriteValue(value.DataType, obj, destination, newOffset);
} }
@ -308,7 +310,7 @@ internal sealed class ExifWriter
private int WriteData(uint startIndex, List<IExifValue> values, Span<byte> destination, int offset) private int WriteData(uint startIndex, List<IExifValue> values, Span<byte> destination, int offset)
{ {
if (this.dataOffsets.Count == 0) if (this.dataOffsets is null || this.dataOffsets.Count == 0)
{ {
return offset; return offset;
} }
@ -428,7 +430,8 @@ internal sealed class ExifWriter
internal static int WriteValue(IExifValue exifValue, Span<byte> destination, int offset) internal static int WriteValue(IExifValue exifValue, Span<byte> destination, int offset)
{ {
object value = exifValue.GetValue(); object? value = exifValue.GetValue();
Guard.NotNull(value);
if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag))
{ {

7
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -23,11 +22,11 @@ internal abstract class ExifArrayValue<TValueType> : ExifValue, IExifValue<TValu
public override bool IsArray => true; public override bool IsArray => 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) if (value is null)
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs

@ -20,7 +20,7 @@ internal sealed class ExifByte : ExifValue<byte>
protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs

@ -16,7 +16,7 @@ internal sealed class ExifByteArray : ExifArrayValue<byte>
public override ExifDataType DataType { get; } public override ExifDataType DataType { get; }
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs

@ -26,7 +26,7 @@ internal sealed class ExifDouble : ExifValue<double>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs

@ -24,7 +24,7 @@ internal sealed class ExifEncodedString : ExifValue<EncodedString>
protected override string StringValue => this.Value.Text; protected override string StringValue => this.Value.Text;
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs

@ -21,7 +21,7 @@ internal sealed class ExifFloat : ExifValue<float>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs

@ -26,7 +26,7 @@ internal sealed class ExifLong : ExifValue<uint>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs

@ -26,7 +26,7 @@ internal sealed class ExifLong8 : ExifValue<ulong>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs

@ -34,7 +34,7 @@ internal sealed class ExifLong8Array : ExifArrayValue<ulong>
} }
} }
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs

@ -32,7 +32,7 @@ internal sealed class ExifNumber : ExifValue<Number>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs

@ -34,7 +34,7 @@ internal sealed class ExifNumberArray : ExifArrayValue<Number>
} }
} }
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs

@ -26,7 +26,7 @@ internal sealed class ExifRational : ExifValue<Rational>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs

@ -22,7 +22,7 @@ internal sealed class ExifRationalArray : ExifArrayValue<Rational>
public override ExifDataType DataType => ExifDataType.Rational; public override ExifDataType DataType => ExifDataType.Rational;
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs

@ -26,7 +26,7 @@ internal sealed class ExifShort : ExifValue<ushort>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs

@ -22,7 +22,7 @@ internal sealed class ExifShortArray : ExifArrayValue<ushort>
public override ExifDataType DataType => ExifDataType.Short; public override ExifDataType DataType => ExifDataType.Short;
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs

@ -21,7 +21,7 @@ internal sealed class ExifSignedByte : ExifValue<sbyte>
protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs

@ -21,7 +21,7 @@ internal sealed class ExifSignedShort : ExifValue<short>
protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); 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)) if (base.TrySetValue(value))
{ {

2
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs

@ -17,7 +17,7 @@ internal sealed class ExifSignedShortArray : ExifArrayValue<short>
public override ExifDataType DataType => ExifDataType.SignedShort; public override ExifDataType DataType => ExifDataType.SignedShort;
public override bool TrySetValue(object value) public override bool TrySetValue(object? value)
{ {
if (base.TrySetValue(value)) if (base.TrySetValue(value))
{ {

4
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs

@ -24,9 +24,9 @@ internal sealed class ExifString : ExifValue<string>
public override ExifDataType DataType => ExifDataType.Ascii; 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)) if (base.TrySetValue(value))
{ {

6
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs

@ -22,11 +22,11 @@ internal sealed class ExifUcs2String : ExifValue<string>
public override ExifDataType DataType => ExifDataType.Byte; 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)) if (base.TrySetValue(value))
{ {

11
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -28,7 +27,7 @@ internal abstract class ExifValue : IExifValue, IEquatable<ExifTag>
else else
{ {
// All array types are value types so Clone() is sufficient here. // 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()); this.TrySetValue(array?.Clone());
} }
} }
@ -43,7 +42,7 @@ internal abstract class ExifValue : IExifValue, IEquatable<ExifTag>
public static bool operator !=(ExifValue left, ExifTag right) => !Equals(left, right); 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) if (obj is null)
{ {
@ -69,14 +68,14 @@ internal abstract class ExifValue : IExifValue, IEquatable<ExifTag>
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(ExifTag other) => this.Tag.Equals(other); public bool Equals(ExifTag? other) => this.Tag.Equals(other);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashCode.Combine(this.Tag, this.GetValue()); 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(); public abstract IExifValue DeepClone();
} }

11
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs

@ -1,18 +1,17 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif;
internal static partial class ExifValues 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) 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) switch (tag)
{ {

20
src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -21,16 +20,16 @@ internal abstract class ExifValue<TValueType> : ExifValue, IExifValue<TValueType
{ {
} }
public TValueType Value { get; set; } public TValueType? Value { get; set; }
/// <summary> /// <summary>
/// Gets the value of the current instance as a string. /// Gets the value of the current instance as a string.
/// </summary> /// </summary>
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) if (value is null)
{ {
@ -49,14 +48,5 @@ internal abstract class ExifValue<TValueType> : ExifValue, IExifValue<TValueType
return false; return false;
} }
public override string ToString() public override string? ToString() => ExifTagDescriptionAttribute.TryGetDescription(this.Tag, this.Value, out string? description) ? description : this.StringValue;
{
if (this.Value == null)
{
return null;
}
string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, this.Value);
return description ?? this.StringValue;
}
} }

4
src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs

@ -27,12 +27,12 @@ public interface IExifValue : IDeepCloneable<IExifValue>
/// Gets the value of this exif value. /// Gets the value of this exif value.
/// </summary> /// </summary>
/// <returns>The value of this exif value.</returns> /// <returns>The value of this exif value.</returns>
object GetValue(); object? GetValue();
/// <summary> /// <summary>
/// Sets the value of this exif value. /// Sets the value of this exif value.
/// </summary> /// </summary>
/// <param name="value">The value of this exif value.</param> /// <param name="value">The value of this exif value.</param>
/// <returns>A value indicating whether the value could be set.</returns> /// <returns>A value indicating whether the value could be set.</returns>
bool TrySetValue(object value); bool TrySetValue(object? value);
} }

2
src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs

@ -12,5 +12,5 @@ public interface IExifValue<TValueType> : IExifValue
/// <summary> /// <summary>
/// Gets or sets the value. /// Gets or sets the value.
/// </summary> /// </summary>
TValueType Value { get; set; } TValueType? Value { get; set; }
} }

3
src/ImageSharp/PixelFormats/PixelImplementations/A8.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -140,7 +139,7 @@ public partial struct A8 : IPixel<A8>, IPackedVector<byte>
/// </summary> /// </summary>
/// <param name="obj">The object to compare.</param> /// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns> /// <returns>True if the object is equal to the packed vector.</returns>
public override readonly bool Equals(object obj) => obj is A8 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is A8 other && this.Equals(other);
/// <summary> /// <summary>
/// Compares another A8 packed vector with the packed vector. /// Compares another A8 packed vector with the packed vector.

3
src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -335,7 +334,7 @@ public partial struct Abgr32 : IPixel<Abgr32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Abgr32 abgr32 && this.Equals(abgr32); public override readonly bool Equals(object? obj) => obj is Abgr32 abgr32 && this.Equals(abgr32);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -334,7 +333,7 @@ public partial struct Argb32 : IPixel<Argb32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Argb32 argb32 && this.Equals(argb32); public override readonly bool Equals(object? obj) => obj is Argb32 argb32 && this.Equals(argb32);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -232,7 +231,7 @@ public partial struct Bgr24 : IPixel<Bgr24>
public readonly bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); public readonly bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B);
/// <inheritdoc/> /// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Bgr24 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Bgr24 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
public override readonly string ToString() => $"Bgr24({this.B}, {this.G}, {this.R})"; public override readonly string ToString() => $"Bgr24({this.B}, {this.G}, {this.R})";

3
src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -151,7 +150,7 @@ public partial struct Bgr565 : IPixel<Bgr565>, IPackedVector<ushort>
(this.PackedValue & 0x1F) * (1F / 31F)); (this.PackedValue & 0x1F) * (1F / 31F));
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Bgr565 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Bgr565 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -287,7 +286,7 @@ public partial struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Bgra32 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Bgra32 other && this.Equals(other);
/// <inheritdoc/> /// <inheritdoc/>
public readonly bool Equals(Bgra32 other) => this.Bgra.Equals(other.Bgra); public readonly bool Equals(Bgra32 other) => this.Bgra.Equals(other.Bgra);

3
src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -143,7 +142,7 @@ public partial struct Bgra4444 : IPixel<Bgra4444>, IPackedVector<ushort>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Bgra4444 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Bgra4444 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -141,7 +140,7 @@ public partial struct Byte4 : IPixel<Byte4>, IPackedVector<uint>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Byte4 byte4 && this.Equals(byte4); public override readonly bool Equals(object? obj) => obj is Byte4 byte4 && this.Equals(byte4);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -140,7 +139,7 @@ public partial struct HalfSingle : IPixel<HalfSingle>, IPackedVector<ushort>
public readonly float ToSingle() => HalfTypeHelper.Unpack(this.PackedValue); public readonly float ToSingle() => HalfTypeHelper.Unpack(this.PackedValue);
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is HalfSingle other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is HalfSingle other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -157,7 +156,7 @@ public partial struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
} }
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is HalfVector2 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is HalfVector2 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -149,7 +148,7 @@ public partial struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is HalfVector4 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is HalfVector4 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/L16.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -153,7 +152,7 @@ public partial struct L16 : IPixel<L16>, IPackedVector<ushort>
public void FromRgba64(Rgba64 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); public void FromRgba64(Rgba64 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B);
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is L16 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is L16 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/L8.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -143,7 +142,7 @@ public partial struct L8 : IPixel<L8>, IPackedVector<byte>
ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); ColorNumerics.DownScaleFrom16BitTo8Bit(source.B));
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is L8 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is L8 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/La16.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -80,7 +79,7 @@ public partial struct La16 : IPixel<La16>, IPackedVector<ushort>
public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue); public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is La16 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is La16 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
public override readonly string ToString() => $"La16({this.L}, {this.A})"; public override readonly string ToString() => $"La16({this.L}, {this.A})";

3
src/ImageSharp/PixelFormats/PixelImplementations/La32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -82,7 +81,7 @@ public partial struct La32 : IPixel<La32>, IPackedVector<uint>
public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue); public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is La32 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is La32 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
public override readonly string ToString() => $"La32({this.L}, {this.A})"; public override readonly string ToString() => $"La32({this.L}, {this.A})";

3
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -162,7 +161,7 @@ public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<u
(sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos); (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos);
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedByte2 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is NormalizedByte2 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -154,7 +153,7 @@ public partial struct NormalizedByte4 : IPixel<NormalizedByte4>, IPackedVector<u
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedByte4 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is NormalizedByte4 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -163,7 +162,7 @@ public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector
(short)(this.PackedValue >> 0x10) / MaxPos); (short)(this.PackedValue >> 0x10) / MaxPos);
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedShort2 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is NormalizedShort2 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -155,7 +154,7 @@ public partial struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedShort4 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is NormalizedShort4 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -146,7 +145,7 @@ public partial struct Rg32 : IPixel<Rg32>, IPackedVector<uint>
public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFFFF, (this.PackedValue >> 16) & 0xFFFF) / Max; public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFFFF, (this.PackedValue >> 16) & 0xFFFF) / Max;
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rg32 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Rg32 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -241,7 +240,7 @@ public partial struct Rgb24 : IPixel<Rgb24>
} }
/// <inheritdoc/> /// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Rgb24 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Rgb24 other && this.Equals(other);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -210,7 +209,7 @@ public partial struct Rgb48 : IPixel<Rgb48>
public void FromRgb48(Rgb48 source) => this = source; public void FromRgb48(Rgb48 source) => this = source;
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rgb48 rgb48 && this.Equals(rgb48); public override readonly bool Equals(object? obj) => obj is Rgb48 rgb48 && this.Equals(rgb48);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -141,7 +140,7 @@ public partial struct Rgba1010102 : IPixel<Rgba1010102>, IPackedVector<uint>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rgba1010102 other && this.Equals(other); public override readonly bool Equals(object? obj) => obj is Rgba1010102 other && this.Equals(other);
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

7
src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Globalization; using System.Globalization;
@ -267,7 +266,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
/// The <see cref="bool"/>. /// The <see cref="bool"/>.
/// </returns> /// </returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool TryParseHex(string hex, out Rgba32 result) public static bool TryParseHex(string? hex, out Rgba32 result)
{ {
result = default; result = default;
if (string.IsNullOrWhiteSpace(hex)) if (string.IsNullOrWhiteSpace(hex))
@ -437,7 +436,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Rgba32 rgba32 && this.Equals(rgba32); public override readonly bool Equals(object? obj) => obj is Rgba32 rgba32 && this.Equals(rgba32);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -514,7 +513,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
/// <returns> /// <returns>
/// A rrggbbaa hex value. /// A rrggbbaa hex value.
/// </returns> /// </returns>
private static string ToRgbaHex(string hex) private static string? ToRgbaHex(string hex)
{ {
if (hex[0] == '#') if (hex[0] == '#')
{ {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save