Browse Source

Merge pull request #506 from carbon/master

Improve Code Quality
af/merge-core
James Jackson-South 9 years ago
committed by GitHub
parent
commit
8c29b7c3d5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/ImageSharp/ColorSpaces/CieLab.cs
  2. 7
      src/ImageSharp/ColorSpaces/CieLch.cs
  3. 7
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  4. 7
      src/ImageSharp/ColorSpaces/CieLuv.cs
  5. 7
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  6. 7
      src/ImageSharp/ColorSpaces/CieXyy.cs
  7. 7
      src/ImageSharp/ColorSpaces/CieXyz.cs
  8. 7
      src/ImageSharp/ColorSpaces/Cmyk.cs
  9. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs
  10. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs
  11. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs
  12. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs
  13. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs
  14. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs
  15. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs
  16. 9
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs
  17. 7
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
  18. 7
      src/ImageSharp/ColorSpaces/Hsl.cs
  19. 7
      src/ImageSharp/ColorSpaces/Hsv.cs
  20. 7
      src/ImageSharp/ColorSpaces/HunterLab.cs
  21. 7
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  22. 7
      src/ImageSharp/ColorSpaces/Lms.cs
  23. 7
      src/ImageSharp/ColorSpaces/Rgb.cs
  24. 7
      src/ImageSharp/ColorSpaces/YCbCr.cs
  25. 2
      src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
  26. 4
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  27. 16
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  28. 2
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  29. 17
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  30. 26
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  31. 4
      src/ImageSharp/Formats/Gif/PackedField.cs
  32. 19
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  33. 12
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  34. 7
      src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
  35. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
  36. 11
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  37. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs
  38. 11
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  39. 9
      src/ImageSharp/Formats/Png/PngChunk.cs
  40. 92
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  41. 22
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  42. 48
      src/ImageSharp/Formats/Png/PngHeader.cs
  43. 1
      src/ImageSharp/IImageFrameCollection.cs
  44. 8
      src/ImageSharp/Image.FromStream.cs
  45. 24
      src/ImageSharp/ImageExtensions.cs
  46. 16
      src/ImageSharp/Memory/SpanHelper.cs
  47. 25
      src/ImageSharp/MetaData/ImageProperty.cs
  48. 50
      src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs
  49. 60
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  50. 394
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  51. 281
      src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
  52. 191
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  53. 410
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  54. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
  55. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
  56. 2
      src/ImageSharp/PixelFormats/Alpha8.cs
  57. 2
      src/ImageSharp/PixelFormats/Bgr24.cs
  58. 2
      src/ImageSharp/PixelFormats/Bgra32.cs
  59. 2
      src/ImageSharp/PixelFormats/Bgra4444.cs
  60. 2
      src/ImageSharp/PixelFormats/Bgra5551.cs
  61. 6
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  62. 2
      src/ImageSharp/PixelFormats/HalfVector4.cs
  63. 2
      src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
  64. 2
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  65. 2
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  66. 2
      src/ImageSharp/PixelFormats/NormalizedShort4.cs
  67. 2
      src/ImageSharp/PixelFormats/Rgb24.cs
  68. 1
      src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
  69. 2
      src/ImageSharp/PixelFormats/RgbaVector.cs
  70. 2
      src/ImageSharp/PixelFormats/Short2.cs
  71. 2
      src/ImageSharp/PixelFormats/Short4.cs
  72. 2
      src/ImageSharp/Primitives/DenseMatrix{T}.cs
  73. 275
      src/ImageSharp/Primitives/LongRational.cs
  74. 33
      src/ImageSharp/Primitives/Rational.cs
  75. 33
      src/ImageSharp/Primitives/SignedRational.cs
  76. 16
      tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs
  77. 18
      tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs
  78. 19
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
  79. 12
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs

2
src/ImageSharp/ColorSpaces/CieLab.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary> /// <summary>
/// Represents a <see cref="CieLab"/> that has L, A, B values set to zero. /// Represents a <see cref="CieLab"/> that has L, A, B values set to zero.
/// </summary> /// </summary>
public static readonly CieLab Empty = default(CieLab); public static readonly CieLab Empty = default;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// The backing vector for SIMD support.

7
src/ImageSharp/ColorSpaces/CieLch.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is CieLch) return obj is CieLch other && this.Equals(other);
{
return this.Equals((CieLch)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is CieLchuv) return obj is CieLchuv other && this.Equals(other);
{
return this.Equals((CieLchuv)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -196,12 +196,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is CieLuv) return obj is CieLuv other && this.Equals(other);
{
return this.Equals((CieLuv)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -132,12 +132,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is CieXyChromaticityCoordinates) return obj is CieXyChromaticityCoordinates other && this.Equals(other);
{
return this.Equals((CieXyChromaticityCoordinates)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -148,12 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is CieXyy) return obj is CieXyy other && this.Equals(other);
{
return this.Equals((CieXyy)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -148,12 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is CieXyz) return obj is CieXyz other && this.Equals(other);
{
return this.Equals((CieXyz)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -150,12 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Cmyk) return obj is Cmyk other && this.Equals(other);
{
return this.Equals((Cmyk)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieLab input) public CieXyz Convert(CieLab input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
float l = input.L, a = input.A, b = input.B; float l = input.L, a = input.A, b = input.B;
float fy = (l + 16) / 116F; float fy = (l + 16) / 116F;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
{ {
@ -44,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieXyz input) public CieLab Convert(CieXyz input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z; float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
{ {
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieLch input) public CieLab Convert(CieLch input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, c = input.C, hDegrees = input.H; float l = input.L, c = input.C, hDegrees = input.H;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
{ {
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch Convert(CieLab input) public CieLch Convert(CieLab input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, a = input.A, b = input.B; float l = input.L, a = input.A, b = input.B;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
{ {
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv Convert(CieLchuv input) public CieLuv Convert(CieLchuv input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, c = input.C, hDegrees = input.H; float l = input.L, c = input.C, hDegrees = input.H;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
{ {
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLchuv Convert(CieLuv input) public CieLchuv Convert(CieLuv input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, a = input.U, b = input.V; float l = input.L, a = input.U, b = input.V;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
{ {
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieLuv input) public CieXyz Convert(CieLuv input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
float l = input.L, u = input.U, v = input.V; float l = input.L, u = input.U, v = input.V;

9
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// Represents the chromaticity coordinates of RGB primaries. /// Represents the chromaticity coordinates of RGB primaries.
/// One of the specifiers of <see cref="IRgbWorkingSpace"/>. /// One of the specifiers of <see cref="IRgbWorkingSpace"/>.
/// </summary> /// </summary>
internal struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates> internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct. /// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
@ -76,12 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is RgbPrimariesChromaticityCoordinates) return obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other);
{
return this.Equals((RgbPrimariesChromaticityCoordinates)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs

@ -73,12 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is RgbWorkingSpace) return obj is RgbWorkingSpace other && this.Equals(other);
{
return this.Equals((RgbWorkingSpace)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Hsl.cs

@ -150,12 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Hsl) return obj is Hsl other && this.Equals(other);
{
return this.Equals((Hsl)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Hsv.cs

@ -202,12 +202,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Hsv) return obj is Hsv other && this.Equals(other);
{
return this.Equals((Hsv)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -190,12 +190,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is HunterLab) return obj is HunterLab other && this.Equals(other);
{
return this.Equals((HunterLab)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -182,12 +182,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is LinearRgb) return obj is LinearRgb other && this.Equals(other);
{
return this.Equals((LinearRgb)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Lms.cs

@ -149,12 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Lms) return obj is Lms other && this.Equals(other);
{
return this.Equals((Lms)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Rgb.cs

@ -204,12 +204,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Rgb) return obj is Rgb other && this.Equals(other);
{
return this.Equals((Rgb)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -152,12 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is YCbCr) return obj is YCbCr other && this.Equals(other);
{
return this.Equals((YCbCr)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs

@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// 32 bits per pixel. Each pixel consists of 4 bytes. /// 32 bits per pixel. Each pixel consists of 4 bytes.
/// </summary> /// </summary>
Pixel32 = 4, Pixel32 = 4
} }
} }

4
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream); return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
} }
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/> /// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream) public IImageInfo Identify(Configuration configuration, Stream stream)
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Identify(stream); return new BmpDecoderCore(configuration, this).Identify(stream);
} }

16
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel); var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255); var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = this.memoryManager.AllocateClean2D<byte>(width, height)) using (Buffer2D<byte> buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
{ {
this.UncompressRle8(width, buffer.Span); this.UncompressRle8(width, buffer.Span);
@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel); var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255); var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = this.memoryManager.AllocateManagedByteBuffer(stride)) using (IManagedByteBuffer buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@ -581,13 +581,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.currentStream.Read(data, 0, BmpFileHeader.Size); this.currentStream.Read(data, 0, BmpFileHeader.Size);
this.fileHeader = new BmpFileHeader this.fileHeader = new BmpFileHeader(
{ type: BitConverter.ToInt16(data, 0),
Type = BitConverter.ToInt16(data, 0), fileSize: BitConverter.ToInt32(data, 2),
FileSize = BitConverter.ToInt32(data, 2), reserved: BitConverter.ToInt32(data, 6),
Reserved = BitConverter.ToInt32(data, 6), offset: BitConverter.ToInt32(data, 10));
Offset = BitConverter.ToInt32(data, 10)
};
} }
/// <summary> /// <summary>

2
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;

17
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -55,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel); this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream. // Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
BmpInfoHeader infoHeader = new BmpInfoHeader var infoHeader = new BmpInfoHeader
{ {
HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize, HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize,
Height = image.Height, Height = image.Height,
@ -69,12 +69,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ClrImportant = 0 ClrImportant = 0
}; };
BmpFileHeader fileHeader = new BmpFileHeader var fileHeader = new BmpFileHeader(
{ type: 19778, // BM
Type = 19778, // BM offset: 54,
Offset = 54, reserved: 0,
FileSize = 54 + infoHeader.ImageSize fileSize: 54 + infoHeader.ImageSize);
};
WriteHeader(writer, fileHeader); WriteHeader(writer, fileHeader);
this.WriteInfo(writer, infoHeader); this.WriteInfo(writer, infoHeader);
@ -92,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="fileHeader"> /// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data. /// The <see cref="BmpFileHeader"/> containing the header data.
/// </param> /// </param>
private static void WriteHeader(EndianBinaryWriter writer, BmpFileHeader fileHeader) private static void WriteHeader(EndianBinaryWriter writer, in BmpFileHeader fileHeader)
{ {
writer.Write(fileHeader.Type); writer.Write(fileHeader.Type);
writer.Write(fileHeader.FileSize); writer.Write(fileHeader.FileSize);

26
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -13,35 +13,43 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// All of the other integer values are stored in little-endian format /// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first). /// (i.e. least-significant byte first).
/// </remarks> /// </remarks>
internal sealed class BmpFileHeader internal readonly struct BmpFileHeader
{ {
/// <summary> /// <summary>
/// Defines of the data structure in the bitmap file. /// Defines of the data structure in the bitmap file.
/// </summary> /// </summary>
public const int Size = 14; public const int Size = 14;
public BmpFileHeader(short type, int fileSize, int reserved, int offset)
{
this.Type = type;
this.FileSize = fileSize;
this.Reserved = reserved;
this.Offset = offset;
}
/// <summary> /// <summary>
/// Gets or sets the Bitmap identifier. /// Gets the Bitmap identifier.
/// The field used to identify the bitmap file: 0x42 0x4D /// The field used to identify the bitmap file: 0x42 0x4D
/// (Hex code points for B and M) /// (Hex code points for B and M)
/// </summary> /// </summary>
public short Type { get; set; } public short Type { get; }
/// <summary> /// <summary>
/// Gets or sets the size of the bitmap file in bytes. /// Gets the size of the bitmap file in bytes.
/// </summary> /// </summary>
public int FileSize { get; set; } public int FileSize { get; }
/// <summary> /// <summary>
/// Gets or sets any reserved data; actual value depends on the application /// Gets any reserved data; actual value depends on the application
/// that creates the image. /// that creates the image.
/// </summary> /// </summary>
public int Reserved { get; set; } public int Reserved { get; }
/// <summary> /// <summary>
/// Gets or sets the offset, i.e. starting address, of the byte where /// Gets the offset, i.e. starting address, of the byte where
/// the bitmap data can be found. /// the bitmap data can be found.
/// </summary> /// </summary>
public int Offset { get; set; } public int Offset { get; }
} }
} }

4
src/ImageSharp/Formats/Gif/PackedField.cs

@ -169,9 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
PackedField? field = obj as PackedField?; return obj is PackedField other && this.Equals(other);
return this.Byte == field?.Byte;
} }
/// <inheritdoc/> /// <inheritdoc/>

19
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -241,19 +241,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
var bld = new StringBuilder(); var sb = new StringBuilder();
bld.Append('['); sb.Append('[');
for (int i = 0; i < Size; i++) for (int i = 0; i < Size; i++)
{ {
bld.Append(this[i]); sb.Append(this[i]);
if (i < Size - 1) if (i < Size - 1)
{ {
bld.Append(','); sb.Append(',');
} }
} }
bld.Append(']'); sb.Append(']');
return bld.ToString(); return sb.ToString();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -273,12 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (ReferenceEquals(null, obj)) return obj is Block8x8 other && this.Equals(other);
{
return false;
}
return obj is Block8x8 && this.Equals((Block8x8)obj);
} }
/// <inheritdoc /> /// <inheritdoc />

12
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -496,19 +496,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
var bld = new StringBuilder(); var sb = new StringBuilder();
bld.Append('['); sb.Append('[');
for (int i = 0; i < Size; i++) for (int i = 0; i < Size; i++)
{ {
bld.Append(this[i]); sb.Append(this[i]);
if (i < Size - 1) if (i < Size - 1)
{ {
bld.Append(','); sb.Append(',');
} }
} }
bld.Append(']'); sb.Append(']');
return bld.ToString(); return sb.ToString();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs

@ -94,12 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (ReferenceEquals(null, obj)) return obj is AdobeMarker other && this.Equals(other);
{
return false;
}
return obj is AdobeMarker && this.Equals((AdobeMarker)obj);
} }
/// <inheritdoc/> /// <inheritdoc/>

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs

@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
} }
} }
marker = default(JFifMarker); marker = default;
return false; return false;
} }
@ -104,12 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (ReferenceEquals(null, obj)) return obj is JFifMarker other && this.Equals(other);
{
return false;
}
return obj is JFifMarker && this.Equals((JFifMarker)obj);
} }
/// <inheritdoc/> /// <inheritdoc/>

11
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -427,10 +427,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
if (this.isExif) if (this.isExif)
{ {
ExifValue horizontal = this.MetaData.ExifProfile.GetValue(ExifTag.XResolution); double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizonalTag)
ExifValue vertical = this.MetaData.ExifProfile.GetValue(ExifTag.YResolution); ? ((Rational)horizonalTag.Value).ToDouble()
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0) if (horizontalValue > 0 && verticalValue > 0)
{ {

6
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary> /// <summary>
/// Represents a jpeg file marker /// Represents a jpeg file marker
/// </summary> /// </summary>
internal struct PdfJsFileMarker internal readonly struct PdfJsFileMarker
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PdfJsFileMarker"/> struct. /// Initializes a new instance of the <see cref="PdfJsFileMarker"/> struct.
@ -34,9 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the current marker is invalid /// Gets a value indicating whether the current marker is invalid
/// </summary> /// </summary>
public bool Invalid { get; set; } public bool Invalid { get; }
/// <summary> /// <summary>
/// Gets the position of the marker within a stream /// Gets the position of the marker within a stream

11
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -380,10 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{ {
if (this.isExif) if (this.isExif)
{ {
ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); double horizontalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); ? ((Rational)horizontalTag.Value).ToDouble()
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double verticalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0) if (horizontalValue > 0 && verticalValue > 0)
{ {

9
src/ImageSharp/Formats/Png/PngChunk.cs

@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
internal sealed class PngChunk internal sealed class PngChunk
{ {
public PngChunk(int length)
{
this.Length = length;
}
/// <summary> /// <summary>
/// Gets or sets the length. /// Gets the length.
/// An unsigned integer giving the number of bytes in the chunk's /// An unsigned integer giving the number of bytes in the chunk's
/// data field. The length counts only the data field, not itself, /// data field. The length counts only the data field, not itself,
/// the chunk type code, or the CRC. Zero is a valid length /// the chunk type code, or the CRC. Zero is a valid length
/// </summary> /// </summary>
public int Length { get; set; } public int Length { get; }
/// <summary> /// <summary>
/// Gets or sets the chunk type as string with 4 chars. /// Gets or sets the chunk type as string with 4 chars.

92
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -217,19 +217,18 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
using (var deframeStream = new ZlibInflateStream(this.currentStream)) using (var deframeStream = new ZlibInflateStream(this.currentStream))
{ {
PngChunk currentChunk; while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{ {
try try
{ {
switch (currentChunk.Type) switch (chunk.Type)
{ {
case PngChunkTypes.Header: case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array); this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break; break;
case PngChunkTypes.Data: case PngChunkTypes.Data:
if (image == null) if (image == null)
@ -237,23 +236,23 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image); this.InitializeImage(metadata, out image);
} }
deframeStream.AllocateNewBytes(currentChunk.Length); deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.currentStream.Read(this.crcBuffer, 0, 4); this.currentStream.Read(this.crcBuffer, 0, 4);
break; break;
case PngChunkTypes.Palette: case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length]; byte[] pal = new byte[chunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal; this.palette = pal;
break; break;
case PngChunkTypes.PaletteAlpha: case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length]; byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha; this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha); this.AssignTransparentMarkers(alpha);
break; break;
case PngChunkTypes.Text: case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkTypes.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
@ -263,10 +262,10 @@ namespace SixLabors.ImageSharp.Formats.Png
finally finally
{ {
// Data is rented in ReadChunkData() // Data is rented in ReadChunkData()
if (currentChunk.Data != null) if (chunk.Data != null)
{ {
currentChunk.Data.Dispose(); chunk.Data.Dispose();
currentChunk.Data = null; chunk.Data = null;
} }
} }
} }
@ -297,25 +296,24 @@ namespace SixLabors.ImageSharp.Formats.Png
this.currentStream.Skip(8); this.currentStream.Skip(8);
try try
{ {
PngChunk currentChunk; while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{ {
try try
{ {
switch (currentChunk.Type) switch (chunk.Type)
{ {
case PngChunkTypes.Header: case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array); this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break; break;
case PngChunkTypes.Data: case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk); this.SkipChunkDataAndCrc(chunk);
break; break;
case PngChunkTypes.Text: case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkTypes.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
@ -325,9 +323,9 @@ namespace SixLabors.ImageSharp.Formats.Png
finally finally
{ {
// Data is rented in ReadChunkData() // Data is rented in ReadChunkData()
if (currentChunk.Data != null) if (chunk.Data != null)
{ {
ArrayPool<byte>.Shared.Return(currentChunk.Data.Array); ArrayPool<byte>.Shared.Return(chunk.Data.Array);
} }
} }
} }
@ -338,7 +336,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.previousScanline?.Dispose(); this.previousScanline?.Dispose();
} }
if (this.header == null) if (this.header.Width == 0 && this.header.Height == 0)
{ {
throw new ImageFormatException("PNG Image does not contain a header chunk"); throw new ImageFormatException("PNG Image does not contain a header chunk");
} }
@ -1159,16 +1157,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param> /// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param>
private void ReadHeaderChunk(ReadOnlySpan<byte> data) private void ReadHeaderChunk(ReadOnlySpan<byte> data)
{ {
this.header = new PngHeader this.header = new PngHeader(
{ width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)),
Width = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)), height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
Height = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)), bitDepth: data[8],
BitDepth = data[8], colorType: (PngColorType)data[9],
ColorType = (PngColorType)data[9], compressionMethod: data[10],
CompressionMethod = data[10], filterMethod: data[11],
FilterMethod = data[11], interlaceMethod: (PngInterlaceMode)data[12]);
InterlaceMethod = (PngInterlaceMode)data[12]
};
} }
/// <summary> /// <summary>
@ -1208,36 +1204,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns> /// <returns>
/// The <see cref="PngChunk"/>. /// The <see cref="PngChunk"/>.
/// </returns> /// </returns>
private PngChunk ReadChunk() private bool TryReadChunk(out PngChunk chunk)
{ {
var chunk = new PngChunk(); int length = this.ReadChunkLength();
this.ReadChunkLength(chunk);
if (chunk.Length == -1) if (length == -1)
{ {
chunk = default;
// IEND // IEND
return null; return false;
} }
chunk = new PngChunk(length);
if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position) if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position)
{ {
// Not a valid chunk so we skip back all but one of the four bytes we have just read. // Not a valid chunk so we skip back all but one of the four bytes we have just read.
// That lets us read one byte at a time until we reach a known chunk. // That lets us read one byte at a time until we reach a known chunk.
this.currentStream.Position -= 3; this.currentStream.Position -= 3;
return chunk;
return true;
} }
this.ReadChunkType(chunk); this.ReadChunkType(chunk);
if (chunk.Type == PngChunkTypes.Data) if (chunk.Type == PngChunkTypes.Data)
{ {
return chunk; return true;
} }
this.ReadChunkData(chunk); this.ReadChunkData(chunk);
this.ReadChunkCrc(chunk); this.ReadChunkCrc(chunk);
return chunk; return true;
} }
/// <summary> /// <summary>
@ -1314,21 +1314,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Calculates the length of the given chunk. /// Calculates the length of the given chunk.
/// </summary> /// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid. /// Thrown if the input stream is not valid.
/// </exception> /// </exception>
private void ReadChunkLength(PngChunk chunk) private int ReadChunkLength()
{ {
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4); int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
if (numBytes < 4) if (numBytes < 4)
{ {
chunk.Length = -1; return -1;
return;
} }
chunk.Length = BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer); return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
} }
/// <summary> /// <summary>

22
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -212,16 +212,14 @@ namespace SixLabors.ImageSharp.Formats.Png
this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerPixel = this.CalculateBytesPerPixel();
var header = new PngHeader var header = new PngHeader(
{ width: image.Width,
Width = image.Width, height: image.Height,
Height = image.Height, colorType: this.pngColorType,
ColorType = this.pngColorType, bitDepth: this.bitDepth,
BitDepth = this.bitDepth, filterMethod: 0, // None
FilterMethod = 0, // None compressionMethod: 0,
CompressionMethod = 0, interlaceMethod: 0);
InterlaceMethod = 0
};
this.WriteHeaderChunk(stream, header); this.WriteHeaderChunk(stream, header);
@ -415,7 +413,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param> /// <param name="header">The <see cref="PngHeader"/>.</param>
private void WriteHeaderChunk(Stream stream, PngHeader header) private void WriteHeaderChunk(Stream stream, in PngHeader header)
{ {
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), header.Width); BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), header.Width);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), header.Height); BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), header.Height);
@ -436,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param> /// <param name="header">The <see cref="PngHeader"/>.</param>
/// <param name="quantized">The quantized frame.</param> /// <param name="quantized">The quantized frame.</param>
private void WritePaletteChunk<TPixel>(Stream stream, PngHeader header, QuantizedFrame<TPixel> quantized) private void WritePaletteChunk<TPixel>(Stream stream, in PngHeader header, QuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// Grab the palette and write it to the stream. // Grab the palette and write it to the stream.

48
src/ImageSharp/Formats/Png/PngHeader.cs

@ -6,55 +6,73 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Represents the png header chunk. /// Represents the png header chunk.
/// </summary> /// </summary>
internal sealed class PngHeader internal readonly struct PngHeader
{ {
public PngHeader(
int width,
int height,
byte bitDepth,
PngColorType colorType,
byte compressionMethod,
byte filterMethod,
PngInterlaceMode interlaceMethod)
{
this.Width = width;
this.Height = height;
this.BitDepth = bitDepth;
this.ColorType = colorType;
this.CompressionMethod = compressionMethod;
this.FilterMethod = filterMethod;
this.InterlaceMethod = interlaceMethod;
}
/// <summary> /// <summary>
/// Gets or sets the dimension in x-direction of the image in pixels. /// Gets the dimension in x-direction of the image in pixels.
/// </summary> /// </summary>
public int Width { get; set; } public int Width { get; }
/// <summary> /// <summary>
/// Gets or sets the dimension in y-direction of the image in pixels. /// Gets the dimension in y-direction of the image in pixels.
/// </summary> /// </summary>
public int Height { get; set; } public int Height { get; }
/// <summary> /// <summary>
/// Gets or sets the bit depth. /// Gets the bit depth.
/// Bit depth is a single-byte integer giving the number of bits per sample /// Bit depth is a single-byte integer giving the number of bits per sample
/// or per palette index (not per pixel). Valid values are 1, 2, 4, 8, and 16, /// or per palette index (not per pixel). Valid values are 1, 2, 4, 8, and 16,
/// although not all values are allowed for all color types. /// although not all values are allowed for all color types.
/// </summary> /// </summary>
public byte BitDepth { get; set; } public byte BitDepth { get; }
/// <summary> /// <summary>
/// Gets or sets the color type. /// Gets the color type.
/// Color type is a integer that describes the interpretation of the /// Color type is a integer that describes the interpretation of the
/// image data. Color type codes represent sums of the following values: /// image data. Color type codes represent sums of the following values:
/// 1 (palette used), 2 (color used), and 4 (alpha channel used). /// 1 (palette used), 2 (color used), and 4 (alpha channel used).
/// </summary> /// </summary>
public PngColorType ColorType { get; set; } public PngColorType ColorType { get; }
/// <summary> /// <summary>
/// Gets or sets the compression method. /// Gets the compression method.
/// Indicates the method used to compress the image data. At present, /// Indicates the method used to compress the image data. At present,
/// only compression method 0 (deflate/inflate compression with a sliding /// only compression method 0 (deflate/inflate compression with a sliding
/// window of at most 32768 bytes) is defined. /// window of at most 32768 bytes) is defined.
/// </summary> /// </summary>
public byte CompressionMethod { get; set; } public byte CompressionMethod { get; }
/// <summary> /// <summary>
/// Gets or sets the preprocessing method. /// Gets the preprocessing method.
/// Indicates the preprocessing method applied to the image /// Indicates the preprocessing method applied to the image
/// data before compression. At present, only filter method 0 /// data before compression. At present, only filter method 0
/// (adaptive filtering with five basic filter types) is defined. /// (adaptive filtering with five basic filter types) is defined.
/// </summary> /// </summary>
public byte FilterMethod { get; set; } public byte FilterMethod { get; }
/// <summary> /// <summary>
/// Gets or sets the transmission order. /// Gets the transmission order.
/// Indicates the transmission order of the image data. /// Indicates the transmission order of the image data.
/// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace). /// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace).
/// </summary> /// </summary>
public PngInterlaceMode InterlaceMethod { get; set; } public PngInterlaceMode InterlaceMethod { get; }
} }
} }

1
src/ImageSharp/IImageFrameCollection.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;

8
src/ImageSharp/Image.FromStream.cs

@ -183,15 +183,15 @@ namespace SixLabors.ImageSharp
return data.img; return data.img;
} }
var stringBuilder = new StringBuilder(); var sb = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageFormatsManager.ImageDecoders) foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageFormatsManager.ImageDecoders)
{ {
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
} }
throw new NotSupportedException(stringBuilder.ToString()); throw new NotSupportedException(sb.ToString());
} }
private static T WithSeekableStream<T>(Configuration config, Stream stream, Func<Stream, T> action) private static T WithSeekableStream<T>(Configuration config, Stream stream, Func<Stream, T> action)

24
src/ImageSharp/ImageExtensions.cs

@ -35,28 +35,28 @@ namespace SixLabors.ImageSharp
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext); IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format == null) if (format == null)
{ {
var stringBuilder = new StringBuilder(); var sb = new StringBuilder();
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{ {
stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
} }
throw new NotSupportedException(stringBuilder.ToString()); throw new NotSupportedException(sb.ToString());
} }
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
if (encoder == null) if (encoder == null)
{ {
var stringBuilder = new StringBuilder(); var sb = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders) foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{ {
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
} }
throw new NotSupportedException(stringBuilder.ToString()); throw new NotSupportedException(sb.ToString());
} }
source.Save(filePath, encoder); source.Save(filePath, encoder);
@ -97,15 +97,15 @@ namespace SixLabors.ImageSharp
if (encoder == null) if (encoder == null)
{ {
var stringBuilder = new StringBuilder(); var sb = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); sb.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{ {
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
} }
throw new NotSupportedException(stringBuilder.ToString()); throw new NotSupportedException(sb.ToString());
} }
source.Save(stream, encoder); source.Save(stream, encoder);

16
src/ImageSharp/Memory/SpanHelper.cs

@ -11,20 +11,6 @@ namespace SixLabors.ImageSharp.Memory
/// </summary> /// </summary>
internal static class SpanHelper internal static class SpanHelper
{ {
/// <summary>
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The <see cref="Span{T}"/> to copy elements from.</param>
/// <param name="destination">The destination <see cref="Span{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(ReadOnlySpan<T> source, Span<T> destination, int count)
where T : struct
{
source.Slice(0, count).CopyTo(destination);
}
/// <summary> /// <summary>
/// Copy all elements of 'source' into 'destination'. /// Copy all elements of 'source' into 'destination'.
/// </summary> /// </summary>
@ -35,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory
public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination) public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct where T : struct
{ {
Copy(source, destination, Math.Min(source.Length, destination.Length)); source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination);
} }
/// <summary> /// <summary>

25
src/ImageSharp/MetaData/ImageProperty.cs

@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.MetaData
/// the copyright information, the date, where the image was created /// the copyright information, the date, where the image was created
/// or some other information. /// or some other information.
/// </summary> /// </summary>
public class ImageProperty : IEquatable<ImageProperty> public readonly struct ImageProperty : IEquatable<ImageProperty>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class. /// Initializes a new instance of the <see cref="ImageProperty"/> struct.
/// </summary> /// </summary>
/// <param name="name">The name of the property.</param> /// <param name="name">The name of the property.</param>
/// <param name="value">The value of the property.</param> /// <param name="value">The value of the property.</param>
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.MetaData
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class /// Initializes a new instance of the <see cref="ImageProperty"/> struct
/// by making a copy from another property. /// by making a copy from another property.
/// </summary> /// </summary>
/// <param name="other"> /// <param name="other">
@ -71,11 +71,6 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns> /// </returns>
public static bool operator ==(ImageProperty left, ImageProperty right) public static bool operator ==(ImageProperty left, ImageProperty right)
{ {
if (ReferenceEquals(left, right))
{
return true;
}
return left.Equals(right); return left.Equals(right);
} }
@ -110,9 +105,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns> /// </returns>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
ImageProperty other = obj as ImageProperty; return obj is ImageProperty other && this.Equals(other);
return this.Equals(other);
} }
/// <summary> /// <summary>
@ -155,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData
/// <param name="other">An object to compare with this object.</param> /// <param name="other">An object to compare with this object.</param>
public bool Equals(ImageProperty other) public bool Equals(ImageProperty other)
{ {
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return this.Name.Equals(other.Name) && Equals(this.Value, other.Value); return this.Name.Equals(other.Name) && Equals(this.Value, other.Value);
} }
} }

50
src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs

@ -11,66 +11,66 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Unknown /// Unknown
/// </summary> /// </summary>
Unknown, Unknown = 0,
/// <summary> /// <summary>
/// Byte /// An 8-bit unsigned integer.
/// </summary> /// </summary>
Byte, Byte = 1,
/// <summary> /// <summary>
/// Ascii /// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
/// </summary> /// </summary>
Ascii, Ascii = 2,
/// <summary> /// <summary>
/// Short /// A 16-bit (2-byte) unsigned integer.
/// </summary> /// </summary>
Short, Short = 3,
/// <summary> /// <summary>
/// Long /// A 32-bit (4-byte) unsigned integer.
/// </summary> /// </summary>
Long, Long = 4,
/// <summary> /// <summary>
/// Rational /// Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
/// </summary> /// </summary>
Rational, Rational = 5,
/// <summary> /// <summary>
/// SignedByte /// An 8-bit signed integer.
/// </summary> /// </summary>
SignedByte, SignedByte = 6,
/// <summary> /// <summary>
/// Undefined /// An 8-bit byte that can take any value depending on the field definition.
/// </summary> /// </summary>
Undefined, Undefined = 7,
/// <summary> /// <summary>
/// SignedShort /// A 16-bit (2-byte) signed integer.
/// </summary> /// </summary>
SignedShort, SignedShort = 8,
/// <summary> /// <summary>
/// SignedLong /// A 32-bit (4-byte) signed integer (2's complement notation).
/// </summary> /// </summary>
SignedLong, SignedLong = 9,
/// <summary> /// <summary>
/// SignedRational /// Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
/// </summary> /// </summary>
SignedRational, SignedRational = 10,
/// <summary> /// <summary>
/// SingleFloat /// A 32-bit floating point value.
/// </summary> /// </summary>
SingleFloat, SingleFloat = 11,
/// <summary> /// <summary>
/// DoubleFloat /// A 64-bit floating point value.
/// </summary> /// </summary>
DoubleFloat DoubleFloat = 12
} }
} }

60
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// The collection of EXIF values /// The collection of EXIF values
/// </summary> /// </summary>
private Collection<ExifValue> values; private List<ExifValue> values;
/// <summary> /// <summary>
/// The list of invalid EXIF tags /// The list of invalid EXIF tags
/// </summary> /// </summary>
private List<ExifTag> invalidTags; private IReadOnlyList<ExifTag> invalidTags;
/// <summary> /// <summary>
/// The thumbnail offset position in the byte stream /// The thumbnail offset position in the byte stream
@ -75,8 +75,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags = new List<ExifTag>(other.invalidTags); this.invalidTags = new List<ExifTag>(other.invalidTags);
if (other.values != null) if (other.values != null)
{ {
this.values = new Collection<ExifValue>(); this.values = new List<ExifValue>(other.Values.Count);
foreach (ExifValue value in other.values)
foreach (ExifValue value in other.Values)
{ {
this.values.Add(new ExifValue(value)); this.values.Add(new ExifValue(value));
} }
@ -92,21 +93,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Gets or sets which parts will be written when the profile is added to an image. /// Gets or sets which parts will be written when the profile is added to an image.
/// </summary> /// </summary>
public ExifParts Parts public ExifParts Parts { get; set; }
{
get;
set;
}
/// <summary> /// <summary>
/// Gets the tags that where found but contained an invalid value. /// Gets the tags that where found but contained an invalid value.
/// </summary> /// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags; public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary> /// <summary>
/// Gets the values of this EXIF profile. /// Gets the values of this EXIF profile.
/// </summary> /// </summary>
public IEnumerable<ExifValue> Values public IReadOnlyList<ExifValue> Values
{ {
get get
{ {
@ -163,6 +160,31 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null; return null;
} }
/// <summary>
/// Conditionally returns the value of the tag if it exists.
/// </summary>
/// <param name="tag">The tag of the EXIF value.</param>
/// <param name="value">The value of the tag, if found.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public bool TryGetValue(ExifTag tag, out ExifValue value)
{
foreach (ExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
value = exifValue;
return true;
}
}
value = default;
return false;
}
/// <summary> /// <summary>
/// Removes the value with the specified tag. /// Removes the value with the specified tag.
/// </summary> /// </summary>
@ -193,16 +215,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public void SetValue(ExifTag tag, object value) public void SetValue(ExifTag tag, object value)
{ {
foreach (ExifValue exifValue in this.Values) for (int i = 0; i < this.Values.Count; i++)
{ {
if (exifValue.Tag == tag) if (this.values[i].Tag == tag)
{ {
exifValue.Value = value; this.values[i] = this.values[i].WithValue(value);
return; return;
} }
} }
var newExifValue = ExifValue.Create(tag, value); var newExifValue = ExifValue.Create(tag, value);
this.values.Add(newExifValue); this.values.Add(newExifValue);
} }
@ -262,12 +286,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.data == null) if (this.data == null)
{ {
this.values = new Collection<ExifValue>(); this.values = new List<ExifValue>();
return; return;
} }
var reader = new ExifReader(); var reader = new ExifReader(this.data);
this.values = reader.Read(this.data);
this.values = reader.ReadValues();
this.invalidTags = new List<ExifTag>(reader.InvalidTags); this.invalidTags = new List<ExifTag>(reader.InvalidTags);
this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailOffset = (int)reader.ThumbnailOffset;
this.thumbnailLength = (int)reader.ThumbnailLength; this.thumbnailLength = (int)reader.ThumbnailLength;

394
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -2,10 +2,13 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
@ -15,38 +18,37 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary> /// </summary>
internal sealed class ExifReader internal sealed class ExifReader
{ {
private readonly Collection<ExifTag> invalidTags = new Collection<ExifTag>(); private readonly List<ExifTag> invalidTags = new List<ExifTag>();
private byte[] exifData; private readonly byte[] exifData;
private uint currentIndex; private int position;
private bool isLittleEndian; private Endianness endianness = Endianness.BigEndian;
private uint exifOffset; private uint exifOffset;
private uint gpsOffset; private uint gpsOffset;
private uint startIndex; private int startIndex;
private delegate TDataType ConverterMethod<TDataType>(byte[] data); public ExifReader(byte[] exifData)
{
DebugGuard.NotNull(exifData, nameof(exifData));
this.exifData = exifData;
}
private delegate TDataType ConverterMethod<TDataType>(ReadOnlySpan<byte> data);
/// <summary> /// <summary>
/// Gets the invalid tags. /// Gets the invalid tags.
/// </summary> /// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags; public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary> /// <summary>
/// Gets the thumbnail length in the byte stream /// Gets the thumbnail length in the byte stream
/// </summary> /// </summary>
public uint ThumbnailLength public uint ThumbnailLength { get; private set; }
{
get;
private set;
}
/// <summary> /// <summary>
/// Gets the thumbnail offset position in the byte stream /// Gets the thumbnail offset position in the byte stream
/// </summary> /// </summary>
public uint ThumbnailOffset public uint ThumbnailOffset { get; private set; }
{
get;
private set;
}
/// <summary> /// <summary>
/// Gets the remaining length. /// Gets the remaining length.
@ -55,81 +57,78 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
get get
{ {
if (this.currentIndex >= this.exifData.Length) if (this.position >= this.exifData.Length)
{ {
return 0; return 0;
} }
return this.exifData.Length - (int)this.currentIndex; return this.exifData.Length - this.position;
} }
} }
/// <summary> /// <summary>
/// Reads and returns the collection of EXIF values. /// Reads and returns the collection of EXIF values.
/// </summary> /// </summary>
/// <param name="data">The data.</param>
/// <returns> /// <returns>
/// The <see cref="Collection{ExifValue}"/>. /// The <see cref="Collection{ExifValue}"/>.
/// </returns> /// </returns>
public Collection<ExifValue> Read(byte[] data) public List<ExifValue> ReadValues()
{ {
DebugGuard.NotNull(data, nameof(data)); var values = new List<ExifValue>();
var result = new Collection<ExifValue>();
this.exifData = data; if (this.ReadString(4) == "Exif")
if (this.GetString(4) == "Exif")
{ {
if (this.GetShort() != 0) if (this.ReadUInt16() != 0)
{ {
return result; return values;
} }
this.startIndex = 6; this.startIndex = 6;
} }
else else
{ {
this.currentIndex = 0; this.position = 0;
} }
this.isLittleEndian = this.GetString(2) == "II"; if (this.ReadString(2) == "II")
{
this.endianness = Endianness.LittleEndian;
}
if (this.GetShort() != 0x002A) if (this.ReadUInt16() != 0x002A)
{ {
return result; return values;
} }
uint ifdOffset = this.GetLong(); uint ifdOffset = this.ReadUInt32();
this.AddValues(result, ifdOffset); this.AddValues(values, (int)ifdOffset);
uint thumbnailOffset = this.GetLong(); uint thumbnailOffset = this.ReadUInt32();
this.GetThumbnail(thumbnailOffset); this.GetThumbnail((int)thumbnailOffset);
if (this.exifOffset != 0) if (this.exifOffset != 0)
{ {
this.AddValues(result, this.exifOffset); this.AddValues(values, (int)this.exifOffset);
} }
if (this.gpsOffset != 0) if (this.gpsOffset != 0)
{ {
this.AddValues(result, this.gpsOffset); this.AddValues(values, (int)this.gpsOffset);
} }
return result; return values;
} }
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, byte[] data, ConverterMethod<TDataType> converter) private static TDataType[] ToArray<TDataType>(ExifDataType dataType, ReadOnlySpan<byte> data, ConverterMethod<TDataType> converter)
{ {
int dataTypeSize = (int)ExifValue.GetSize(dataType); int dataTypeSize = (int)ExifValue.GetSize(dataType);
int length = data.Length / dataTypeSize; int length = data.Length / dataTypeSize;
TDataType[] result = new TDataType[length]; var result = new TDataType[length];
byte[] buffer = new byte[dataTypeSize];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
Array.Copy(data, i * dataTypeSize, buffer, 0, dataTypeSize); ReadOnlySpan<byte> buffer = data.Slice(i * dataTypeSize, dataTypeSize);
result.SetValue(converter(buffer), i); result.SetValue(converter(buffer), i);
} }
@ -137,14 +136,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result; return result;
} }
private static byte ToByte(byte[] data) private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
{
return data[0];
}
private static string ToString(byte[] data) private unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
{ {
string result = Encoding.UTF8.GetString(data, 0, data.Length); #if NETSTANDARD1_1
byte[] bytes = buffer.ToArray();
string result = Encoding.UTF8.GetString(bytes, 0, buffer.Length);
#else
string result;
fixed (byte* pointer = &MemoryMarshal.GetReference(buffer))
{
result = Encoding.UTF8.GetString(pointer, buffer.Length);
}
#endif
int nullCharIndex = result.IndexOf('\0'); int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1) if (nullCharIndex != -1)
{ {
@ -159,15 +167,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary> /// </summary>
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
private void AddValues(Collection<ExifValue> values, uint index) private void AddValues(List<ExifValue> values, int index)
{ {
this.currentIndex = this.startIndex + index; this.position = this.startIndex + index;
ushort count = this.GetShort(); int count = this.ReadUInt16();
for (ushort i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ExifValue value = this.CreateValue(); if (!this.TryReadValue(out ExifValue value))
if (value == null)
{ {
continue; continue;
} }
@ -208,9 +215,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
} }
} }
private object ConvertValue(ExifDataType dataType, byte[] data, uint numberOfComponents) private object ConvertValue(ExifDataType dataType, ReadOnlySpan<byte> buffer, uint numberOfComponents)
{ {
if (data == null || data.Length == 0) if (buffer == null || buffer.Length == 0)
{ {
return null; return null;
} }
@ -220,106 +227,116 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifDataType.Unknown: case ExifDataType.Unknown:
return null; return null;
case ExifDataType.Ascii: case ExifDataType.Ascii:
return ToString(data); return this.ConvertToString(buffer);
case ExifDataType.Byte: case ExifDataType.Byte:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return ToByte(data); return this.ConvertToByte(buffer);
} }
return data; return buffer.ToArray();
case ExifDataType.DoubleFloat: case ExifDataType.DoubleFloat:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToDouble(data); return this.ConvertToDouble(buffer);
} }
return ToArray(dataType, data, this.ToDouble); return ToArray(dataType, buffer, this.ConvertToDouble);
case ExifDataType.Long: case ExifDataType.Long:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToLong(data); return this.ConvertToUInt32(buffer);
} }
return ToArray(dataType, data, this.ToLong); return ToArray(dataType, buffer, this.ConvertToUInt32);
case ExifDataType.Rational: case ExifDataType.Rational:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToRational(data); return this.ToRational(buffer);
} }
return ToArray(dataType, data, this.ToRational); return ToArray(dataType, buffer, this.ToRational);
case ExifDataType.Short: case ExifDataType.Short:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToShort(data); return this.ConvertToShort(buffer);
} }
return ToArray(dataType, data, this.ToShort); return ToArray(dataType, buffer, this.ConvertToShort);
case ExifDataType.SignedByte: case ExifDataType.SignedByte:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToSignedByte(data); return this.ConvertToSignedByte(buffer);
} }
return ToArray(dataType, data, this.ToSignedByte); return ToArray(dataType, buffer, this.ConvertToSignedByte);
case ExifDataType.SignedLong: case ExifDataType.SignedLong:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToSignedLong(data); return this.ConvertToInt32(buffer);
} }
return ToArray(dataType, data, this.ToSignedLong); return ToArray(dataType, buffer, this.ConvertToInt32);
case ExifDataType.SignedRational: case ExifDataType.SignedRational:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToSignedRational(data); return this.ToSignedRational(buffer);
} }
return ToArray(dataType, data, this.ToSignedRational); return ToArray(dataType, buffer, this.ToSignedRational);
case ExifDataType.SignedShort: case ExifDataType.SignedShort:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToSignedShort(data); return this.ConvertToSignedShort(buffer);
} }
return ToArray(dataType, data, this.ToSignedShort); return ToArray(dataType, buffer, this.ConvertToSignedShort);
case ExifDataType.SingleFloat: case ExifDataType.SingleFloat:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return this.ToSingle(data); return this.ConvertToSingle(buffer);
} }
return ToArray(dataType, data, this.ToSingle); return ToArray(dataType, buffer, this.ConvertToSingle);
case ExifDataType.Undefined: case ExifDataType.Undefined:
if (numberOfComponents == 1) if (numberOfComponents == 1)
{ {
return ToByte(data); return this.ConvertToByte(buffer);
} }
return data; return buffer.ToArray();
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
private ExifValue CreateValue() private bool TryReadValue(out ExifValue exifValue)
{ {
// 2 | 2 | 4 | 4
// tag | type | count | value offset
if (this.RemainingLength < 12) if (this.RemainingLength < 12)
{ {
return null; exifValue = default;
return false;
} }
ExifTag tag = this.ToEnum(this.GetShort(), ExifTag.Unknown); ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown);
ExifDataType dataType = this.ToEnum(this.GetShort(), ExifDataType.Unknown); uint type = this.ReadUInt16();
object value;
if (dataType == ExifDataType.Unknown) // Ensure that the data type is valid
if (type == 0 || type > 12)
{ {
return new ExifValue(tag, dataType, null, false); exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false);
return true;
} }
uint numberOfComponents = this.GetLong(); var dataType = (ExifDataType)type;
object value;
uint numberOfComponents = this.ReadUInt32();
// Issue #132: ExifDataType == Undefined is treated like a byte array. // Issue #132: ExifDataType == Undefined is treated like a byte array.
// If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes) // If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes)
@ -329,29 +346,50 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
} }
uint size = numberOfComponents * ExifValue.GetSize(dataType); uint size = numberOfComponents * ExifValue.GetSize(dataType);
byte[] data = this.GetBytes(4);
this.TryReadSpan(4, out ReadOnlySpan<byte> offsetBuffer);
if (size > 4) if (size > 4)
{ {
uint oldIndex = this.currentIndex; int oldIndex = this.position;
this.currentIndex = this.ToLong(data) + this.startIndex;
uint newIndex = this.ConvertToUInt32(offsetBuffer) + (uint)this.startIndex;
// Ensure that the new index does not overrun the data
if (newIndex > int.MaxValue)
{
this.invalidTags.Add(tag);
exifValue = default;
return false;
}
this.position = (int)newIndex;
if (this.RemainingLength < size) if (this.RemainingLength < size)
{ {
this.invalidTags.Add(tag); this.invalidTags.Add(tag);
this.currentIndex = oldIndex; this.position = oldIndex;
return null;
exifValue = default;
return false;
} }
value = this.ConvertValue(dataType, this.GetBytes(size), numberOfComponents); this.TryReadSpan((int)size, out ReadOnlySpan<byte> dataBuffer);
this.currentIndex = oldIndex;
value = this.ConvertValue(dataType, dataBuffer, numberOfComponents);
this.position = oldIndex;
} }
else else
{ {
value = this.ConvertValue(dataType, data, numberOfComponents); value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
} }
bool isArray = value != null && numberOfComponents > 1; exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents > 1);
return new ExifValue(tag, dataType, value, isArray);
return true;
} }
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue) private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
@ -366,51 +404,57 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return defaultValue; return defaultValue;
} }
private byte[] GetBytes(uint length) private bool TryReadSpan(int length, out ReadOnlySpan<byte> span)
{ {
if (this.currentIndex + length > (uint)this.exifData.Length) if (this.RemainingLength < length)
{ {
return null; span = default;
return false;
} }
byte[] data = new byte[length]; span = new ReadOnlySpan<byte>(this.exifData, this.position, length);
Array.Copy(this.exifData, (int)this.currentIndex, data, 0, (int)length);
this.currentIndex += length;
return data; this.position += length;
return true;
} }
private uint GetLong() private uint ReadUInt32()
{ {
return this.ToLong(this.GetBytes(4)); // Known as Long in Exif Specification
return this.TryReadSpan(4, out ReadOnlySpan<byte> span)
? this.ConvertToUInt32(span)
: default;
} }
private ushort GetShort() private ushort ReadUInt16()
{ {
return this.ToShort(this.GetBytes(2)); return this.TryReadSpan(2, out ReadOnlySpan<byte> span)
? this.ConvertToShort(span)
: default;
} }
private string GetString(uint length) private string ReadString(int length)
{ {
byte[] data = this.GetBytes(length); if (this.TryReadSpan(length, out ReadOnlySpan<byte> span) && span.Length != 0)
if (data == null || data.Length == 0)
{ {
return null; return this.ConvertToString(span);
} }
return ToString(data); return null;
} }
private void GetThumbnail(uint offset) private void GetThumbnail(int offset)
{ {
var values = new Collection<ExifValue>(); var values = new List<ExifValue>();
this.AddValues(values, offset); this.AddValues(values, offset);
foreach (ExifValue value in values) foreach (ExifValue value in values)
{ {
if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long)) if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long))
{ {
this.ThumbnailOffset = (uint)value.Value + this.startIndex; this.ThumbnailOffset = (uint)value.Value + (uint)this.startIndex;
} }
else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long) else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long)
{ {
@ -419,120 +463,112 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
} }
} }
private double ToDouble(byte[] data) private unsafe double ConvertToDouble(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 8)) if (buffer.Length < 8)
{ {
return default(double); return default;
} }
return BitConverter.ToDouble(data, 0); long intValue = this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt64BigEndian(buffer)
: BinaryPrimitives.ReadInt64LittleEndian(buffer);
return *((double*)&intValue);
} }
private uint ToLong(byte[] data) private uint ConvertToUInt32(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 4)) // Known as Long in Exif Specification
if (buffer.Length < 4)
{ {
return default(uint); return default;
} }
return BitConverter.ToUInt32(data, 0); return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadUInt32BigEndian(buffer)
: BinaryPrimitives.ReadUInt32LittleEndian(buffer);
} }
private ushort ToShort(byte[] data) private ushort ConvertToShort(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 2)) if (buffer.Length < 2)
{ {
return default(ushort); return default;
} }
return BitConverter.ToUInt16(data, 0); return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadUInt16BigEndian(buffer)
: BinaryPrimitives.ReadUInt16LittleEndian(buffer);
} }
private float ToSingle(byte[] data) private unsafe float ConvertToSingle(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 4)) if (buffer.Length < 4)
{ {
return default(float); return default;
} }
return BitConverter.ToSingle(data, 0); int intValue = this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
return *((float*)&intValue);
} }
private Rational ToRational(byte[] data) private Rational ToRational(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 8, 4)) if (buffer.Length < 8)
{ {
return default(Rational); return default;
} }
uint numerator = BitConverter.ToUInt32(data, 0); uint numerator = this.ConvertToUInt32(buffer.Slice(0, 4));
uint denominator = BitConverter.ToUInt32(data, 4); uint denominator = this.ConvertToUInt32(buffer.Slice(4, 4));
return new Rational(numerator, denominator, false); return new Rational(numerator, denominator, false);
} }
private sbyte ToSignedByte(byte[] data) private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer)
{ {
return unchecked((sbyte)data[0]); return unchecked((sbyte)buffer[0]);
} }
private int ToSignedLong(byte[] data) private int ConvertToInt32(ReadOnlySpan<byte> buffer) // SignedLong in Exif Specification
{ {
if (!this.ValidateArray(data, 4)) if (buffer.Length < 4)
{ {
return default(int); return default;
} }
return BitConverter.ToInt32(data, 0); return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
} }
private SignedRational ToSignedRational(byte[] data) private SignedRational ToSignedRational(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 8, 4)) if (buffer.Length < 8)
{ {
return default(SignedRational); return default;
} }
int numerator = BitConverter.ToInt32(data, 0); int numerator = this.ConvertToInt32(buffer.Slice(0, 4));
int denominator = BitConverter.ToInt32(data, 4); int denominator = this.ConvertToInt32(buffer.Slice(4, 4));
return new SignedRational(numerator, denominator, false); return new SignedRational(numerator, denominator, false);
} }
private short ToSignedShort(byte[] data) private short ConvertToSignedShort(ReadOnlySpan<byte> buffer)
{ {
if (!this.ValidateArray(data, 2)) if (buffer.Length < 2)
{ {
return default(short); return default;
} }
return BitConverter.ToInt16(data, 0); return this.endianness == Endianness.BigEndian
} ? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
private bool ValidateArray(byte[] data, int size)
{
return this.ValidateArray(data, size, size);
}
private bool ValidateArray(byte[] data, int size, int stepSize)
{
if (data == null || data.Length < size)
{
return false;
}
if (this.isLittleEndian == BitConverter.IsLittleEndian)
{
return true;
}
for (int i = 0; i < data.Length; i += stepSize)
{
Array.Reverse(data, i, stepSize);
}
return true;
} }
} }
} }

281
src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs

@ -0,0 +1,281 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using static SixLabors.ImageSharp.MetaData.Profiles.Exif.ExifTag;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
internal static class ExifTags
{
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
public static readonly ExifTag[] Ifd =
{
SubfileType,
OldSubfileType,
ImageWidth,
ImageLength,
BitsPerSample,
Compression,
PhotometricInterpretation,
Thresholding,
CellWidth,
CellLength,
FillOrder,
DocumentName,
ImageDescription,
Make,
Model,
StripOffsets,
Orientation,
SamplesPerPixel,
RowsPerStrip,
StripByteCounts,
MinSampleValue,
MaxSampleValue,
XResolution,
YResolution,
PlanarConfiguration,
PageName,
XPosition,
YPosition,
FreeOffsets,
FreeByteCounts,
GrayResponseUnit,
GrayResponseCurve,
T4Options,
T6Options,
ResolutionUnit,
PageNumber,
ColorResponseUnit,
TransferFunction,
Software,
DateTime,
Artist,
HostComputer,
Predictor,
WhitePoint,
PrimaryChromaticities,
ColorMap,
HalftoneHints,
TileWidth,
TileLength,
TileOffsets,
TileByteCounts,
BadFaxLines,
CleanFaxData,
ConsecutiveBadFaxLines,
InkSet,
InkNames,
NumberOfInks,
DotRange,
TargetPrinter,
ExtraSamples,
SampleFormat,
SMinSampleValue,
SMaxSampleValue,
TransferRange,
ClipPath,
XClipPathUnits,
YClipPathUnits,
Indexed,
JPEGTables,
OPIProxy,
ProfileType,
FaxProfile,
CodingMethods,
VersionYear,
ModeNumber,
Decode,
DefaultImageColor,
T82ptions,
JPEGProc,
JPEGInterchangeFormat,
JPEGInterchangeFormatLength,
JPEGRestartInterval,
JPEGLosslessPredictors,
JPEGPointTransforms,
JPEGQTables,
JPEGDCTables,
JPEGACTables,
YCbCrCoefficients,
YCbCrSubsampling,
YCbCrSubsampling,
YCbCrPositioning,
ReferenceBlackWhite,
StripRowCounts,
XMP,
Rating,
RatingPercent,
ImageID,
CFARepeatPatternDim,
CFAPattern2,
BatteryLevel,
Copyright,
MDFileTag,
MDScalePixel,
MDLabName,
MDSampleInfo,
MDPrepDate,
MDPrepTime,
MDFileUnits,
PixelScale,
IntergraphPacketData,
IntergraphRegisters,
IntergraphMatrix,
ModelTiePoint,
SEMInfo,
ModelTransform,
ImageLayer,
FaxRecvParams,
FaxSubaddress,
FaxRecvTime,
ImageSourceData,
XPTitle,
XPComment,
XPAuthor,
XPKeywords,
XPSubject,
GDALMetadata,
GDALNoData
};
/// <summary>
/// The collection of Exif tags
/// </summary>
public static readonly ExifTag[] Exif =
{
ExposureTime,
FNumber,
ExposureProgram,
SpectralSensitivity,
ISOSpeedRatings,
OECF,
Interlace,
TimeZoneOffset,
SelfTimerMode,
SensitivityType,
StandardOutputSensitivity,
RecommendedExposureIndex,
ISOSpeed,
ISOSpeedLatitudeyyy,
ISOSpeedLatitudezzz,
ExifVersion,
DateTimeOriginal,
DateTimeDigitized,
OffsetTime,
OffsetTimeOriginal,
OffsetTimeDigitized,
ComponentsConfiguration,
CompressedBitsPerPixel,
ShutterSpeedValue,
ApertureValue,
BrightnessValue,
ExposureBiasValue,
MaxApertureValue,
SubjectDistance,
MeteringMode,
LightSource,
Flash,
FocalLength,
FlashEnergy2,
SpatialFrequencyResponse2,
Noise,
FocalPlaneXResolution2,
FocalPlaneYResolution2,
FocalPlaneResolutionUnit2,
ImageNumber,
SecurityClassification,
ImageHistory,
SubjectArea,
ExposureIndex2,
TIFFEPStandardID,
SensingMethod2,
MakerNote,
UserComment,
SubsecTime,
SubsecTimeOriginal,
SubsecTimeDigitized,
AmbientTemperature,
Humidity,
Pressure,
WaterDepth,
Acceleration,
CameraElevationAngle,
FlashpixVersion,
ColorSpace,
PixelXDimension,
PixelYDimension,
RelatedSoundFile,
FlashEnergy,
SpatialFrequencyResponse,
FocalPlaneXResolution,
FocalPlaneYResolution,
FocalPlaneResolutionUnit,
SubjectLocation,
ExposureIndex,
SensingMethod,
FileSource,
SceneType,
CFAPattern,
CustomRendered,
ExposureMode,
WhiteBalance,
DigitalZoomRatio,
FocalLengthIn35mmFilm,
SceneCaptureType,
GainControl,
Contrast,
Saturation,
Sharpness,
DeviceSettingDescription,
SubjectDistanceRange,
ImageUniqueID,
OwnerName,
SerialNumber,
LensInfo,
LensMake,
LensModel,
LensSerialNumber
};
/// <summary>
/// The collection of GPS tags
/// </summary>
public static readonly ExifTag[] Gps =
{
GPSVersionID,
GPSLatitudeRef,
GPSLatitude,
GPSLongitudeRef,
GPSLongitude,
GPSAltitudeRef,
GPSAltitude,
GPSTimestamp,
GPSSatellites,
GPSStatus,
GPSMeasureMode,
GPSDOP,
GPSSpeedRef,
GPSSpeed,
GPSTrackRef,
GPSTrack,
GPSImgDirectionRef,
GPSImgDirection,
GPSMapDatum,
GPSDestLatitudeRef,
GPSDestLatitude,
GPSDestLongitudeRef,
GPSDestLongitude,
GPSDestBearingRef,
GPSDestBearing,
GPSDestDistanceRef,
GPSDestDistance,
GPSProcessingMethod,
GPSAreaInformation,
GPSDateStamp,
GPSDifferential
};
}
}

191
src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs

@ -13,11 +13,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary> /// </summary>
public sealed class ExifValue : IEquatable<ExifValue> public sealed class ExifValue : IEquatable<ExifValue>
{ {
/// <summary>
/// The exif value.
/// </summary>
private object exifValue;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class /// Initializes a new instance of the <see cref="ExifValue"/> class
/// by making a copy from another exif value. /// by making a copy from another exif value.
@ -34,30 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (!other.IsArray) if (!other.IsArray)
{ {
this.exifValue = other.exifValue; this.Value = other.Value;
} }
else else
{ {
var array = (Array)other.exifValue; var array = (Array)other.Value;
this.exifValue = array.Clone(); this.Value = array.Clone();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray;
if (dataType == ExifDataType.Ascii)
{
this.IsArray = false;
} }
} }
@ -69,51 +46,32 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param> /// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray) internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
: this(tag, dataType, isArray)
{ {
this.exifValue = value; this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray && dataType != ExifDataType.Ascii;
this.Value = value;
} }
/// <summary> /// <summary>
/// Gets the data type of the exif value. /// Gets the data type of the exif value.
/// </summary> /// </summary>
public ExifDataType DataType public ExifDataType DataType { get; }
{
get;
}
/// <summary> /// <summary>
/// Gets a value indicating whether the value is an array. /// Gets a value indicating whether the value is an array.
/// </summary> /// </summary>
public bool IsArray public bool IsArray { get; }
{
get;
}
/// <summary> /// <summary>
/// Gets the tag of the exif value. /// Gets the tag of the exif value.
/// </summary> /// </summary>
public ExifTag Tag public ExifTag Tag { get; }
{
get;
}
/// <summary> /// <summary>
/// Gets or sets the value. /// Gets the value.
/// </summary> /// </summary>
public object Value public object Value { get; }
{
get
{
return this.exifValue;
}
set
{
this.CheckValue(value);
this.exifValue = value;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether the EXIF value has a value. /// Gets a value indicating whether the EXIF value has a value.
@ -122,14 +80,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
get get
{ {
if (this.exifValue == null) if (this.Value == null)
{ {
return false; return false;
} }
if (this.DataType == ExifDataType.Ascii) if (this.DataType == ExifDataType.Ascii)
{ {
return ((string)this.exifValue).Length > 0; return ((string)this.Value).Length > 0;
} }
return true; return true;
@ -143,7 +101,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
get get
{ {
if (this.exifValue == null) if (this.Value == null)
{ {
return 4; return 4;
} }
@ -163,12 +121,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
if (this.DataType == ExifDataType.Ascii) if (this.DataType == ExifDataType.Ascii)
{ {
return Encoding.UTF8.GetBytes((string)this.exifValue).Length; return Encoding.UTF8.GetBytes((string)this.Value).Length;
} }
if (this.IsArray) if (this.IsArray)
{ {
return ((Array)this.exifValue).Length; return ((Array)this.Value).Length;
} }
return 1; return 1;
@ -217,12 +175,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (ReferenceEquals(this, obj)) return obj is ExifValue other && this.Equals(other);
{
return true;
}
return this.Equals(obj as ExifValue);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -241,7 +194,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return return
this.Tag == other.Tag && this.Tag == other.Tag &&
this.DataType == other.DataType && this.DataType == other.DataType &&
object.Equals(this.exifValue, other.exifValue); object.Equals(this.Value, other.Value);
}
/// <summary>
/// Clones the current value, overwriting the value.
/// </summary>
/// <param name="value">The value to overwrite.</param>
/// <returns><see cref="ExifValue"/></returns>
public ExifValue WithValue(object value)
{
this.CheckValue(value);
return new ExifValue(this.Tag, this.DataType, value, this.IsArray);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -253,26 +218,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
{ {
if (this.exifValue == null) if (this.Value == null)
{ {
return null; return null;
} }
if (this.DataType == ExifDataType.Ascii) if (this.DataType == ExifDataType.Ascii)
{ {
return (string)this.exifValue; return (string)this.Value;
} }
if (!this.IsArray) if (!this.IsArray)
{ {
return this.ToString(this.exifValue); return this.ToString(this.Value);
} }
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (object value in (Array)this.exifValue) foreach (object value in (Array)this.Value)
{ {
sb.Append(this.ToString(value)); sb.Append(this.ToString(value));
sb.Append(" "); sb.Append(' ');
} }
return sb.ToString(); return sb.ToString();
@ -293,13 +258,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag"); Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag");
ExifValue exifValue;
Type type = value?.GetType();
if (type != null && type.IsArray)
{
type = type.GetElementType();
}
switch (tag) switch (tag)
{ {
case ExifTag.ImageDescription: case ExifTag.ImageDescription:
@ -355,8 +313,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSDestBearingRef: case ExifTag.GPSDestBearingRef:
case ExifTag.GPSDestDistanceRef: case ExifTag.GPSDestDistanceRef:
case ExifTag.GPSDateStamp: case ExifTag.GPSDateStamp:
exifValue = new ExifValue(tag, ExifDataType.Ascii, true); return new ExifValue(tag, ExifDataType.Ascii, value, true);
break;
case ExifTag.ClipPath: case ExifTag.ClipPath:
case ExifTag.VersionYear: case ExifTag.VersionYear:
@ -369,13 +326,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.XPKeywords: case ExifTag.XPKeywords:
case ExifTag.XPSubject: case ExifTag.XPSubject:
case ExifTag.GPSVersionID: case ExifTag.GPSVersionID:
exifValue = new ExifValue(tag, ExifDataType.Byte, true); return new ExifValue(tag, ExifDataType.Byte, value, true);
break;
case ExifTag.FaxProfile: case ExifTag.FaxProfile:
case ExifTag.ModeNumber: case ExifTag.ModeNumber:
case ExifTag.GPSAltitudeRef: case ExifTag.GPSAltitudeRef:
exifValue = new ExifValue(tag, ExifDataType.Byte, false); return new ExifValue(tag, ExifDataType.Byte, value, false);
break;
case ExifTag.FreeOffsets: case ExifTag.FreeOffsets:
case ExifTag.FreeByteCounts: case ExifTag.FreeByteCounts:
@ -389,8 +345,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.StripRowCounts: case ExifTag.StripRowCounts:
case ExifTag.IntergraphRegisters: case ExifTag.IntergraphRegisters:
case ExifTag.TimeZoneOffset: case ExifTag.TimeZoneOffset:
exifValue = new ExifValue(tag, ExifDataType.Long, true); return new ExifValue(tag, ExifDataType.Long, value, true);
break;
case ExifTag.SubfileType: case ExifTag.SubfileType:
case ExifTag.SubIFDOffset: case ExifTag.SubIFDOffset:
case ExifTag.GPSIFDOffset: case ExifTag.GPSIFDOffset:
@ -412,8 +367,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.FaxRecvParams: case ExifTag.FaxRecvParams:
case ExifTag.FaxRecvTime: case ExifTag.FaxRecvTime:
case ExifTag.ImageNumber: case ExifTag.ImageNumber:
exifValue = new ExifValue(tag, ExifDataType.Long, false); return new ExifValue(tag, ExifDataType.Long, value, false);
break;
case ExifTag.WhitePoint: case ExifTag.WhitePoint:
case ExifTag.PrimaryChromaticities: case ExifTag.PrimaryChromaticities:
@ -428,8 +382,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSTimestamp: case ExifTag.GPSTimestamp:
case ExifTag.GPSDestLatitude: case ExifTag.GPSDestLatitude:
case ExifTag.GPSDestLongitude: case ExifTag.GPSDestLongitude:
exifValue = new ExifValue(tag, ExifDataType.Rational, true); return new ExifValue(tag, ExifDataType.Rational, value, true);
break;
case ExifTag.XPosition: case ExifTag.XPosition:
case ExifTag.YPosition: case ExifTag.YPosition:
case ExifTag.XResolution: case ExifTag.XResolution:
@ -463,8 +417,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSImgDirection: case ExifTag.GPSImgDirection:
case ExifTag.GPSDestBearing: case ExifTag.GPSDestBearing:
case ExifTag.GPSDestDistance: case ExifTag.GPSDestDistance:
exifValue = new ExifValue(tag, ExifDataType.Rational, false); return new ExifValue(tag, ExifDataType.Rational, value, false);
break;
case ExifTag.BitsPerSample: case ExifTag.BitsPerSample:
case ExifTag.MinSampleValue: case ExifTag.MinSampleValue:
@ -487,8 +440,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ISOSpeedRatings: case ExifTag.ISOSpeedRatings:
case ExifTag.SubjectArea: case ExifTag.SubjectArea:
case ExifTag.SubjectLocation: case ExifTag.SubjectLocation:
exifValue = new ExifValue(tag, ExifDataType.Short, true); return new ExifValue(tag, ExifDataType.Short, value, true);
break;
case ExifTag.OldSubfileType: case ExifTag.OldSubfileType:
case ExifTag.Compression: case ExifTag.Compression:
case ExifTag.PhotometricInterpretation: case ExifTag.PhotometricInterpretation:
@ -535,20 +488,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.Sharpness: case ExifTag.Sharpness:
case ExifTag.SubjectDistanceRange: case ExifTag.SubjectDistanceRange:
case ExifTag.GPSDifferential: case ExifTag.GPSDifferential:
exifValue = new ExifValue(tag, ExifDataType.Short, false); return new ExifValue(tag, ExifDataType.Short, value, false);
break;
case ExifTag.Decode: case ExifTag.Decode:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, true); return new ExifValue(tag, ExifDataType.SignedRational, value, true);
break;
case ExifTag.ShutterSpeedValue: case ExifTag.ShutterSpeedValue:
case ExifTag.BrightnessValue: case ExifTag.BrightnessValue:
case ExifTag.ExposureBiasValue: case ExifTag.ExposureBiasValue:
case ExifTag.AmbientTemperature: case ExifTag.AmbientTemperature:
case ExifTag.WaterDepth: case ExifTag.WaterDepth:
case ExifTag.CameraElevationAngle: case ExifTag.CameraElevationAngle:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, false); return new ExifValue(tag, ExifDataType.SignedRational, value, false);
break;
case ExifTag.JPEGTables: case ExifTag.JPEGTables:
case ExifTag.OECF: case ExifTag.OECF:
@ -565,18 +516,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ImageSourceData: case ExifTag.ImageSourceData:
case ExifTag.GPSProcessingMethod: case ExifTag.GPSProcessingMethod:
case ExifTag.GPSAreaInformation: case ExifTag.GPSAreaInformation:
exifValue = new ExifValue(tag, ExifDataType.Undefined, true); return new ExifValue(tag, ExifDataType.Undefined, value, true);
break;
case ExifTag.FileSource: case ExifTag.FileSource:
case ExifTag.SceneType: case ExifTag.SceneType:
exifValue = new ExifValue(tag, ExifDataType.Undefined, false); return new ExifValue(tag, ExifDataType.Undefined, value, false);
break;
case ExifTag.StripOffsets: case ExifTag.StripOffsets:
case ExifTag.TileByteCounts: case ExifTag.TileByteCounts:
case ExifTag.ImageLayer: case ExifTag.ImageLayer:
exifValue = CreateNumber(tag, type, true); return CreateNumber(tag, value, true);
break;
case ExifTag.ImageWidth: case ExifTag.ImageWidth:
case ExifTag.ImageLength: case ExifTag.ImageLength:
case ExifTag.TileWidth: case ExifTag.TileWidth:
@ -585,15 +535,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ConsecutiveBadFaxLines: case ExifTag.ConsecutiveBadFaxLines:
case ExifTag.PixelXDimension: case ExifTag.PixelXDimension:
case ExifTag.PixelYDimension: case ExifTag.PixelYDimension:
exifValue = CreateNumber(tag, type, false); return CreateNumber(tag, value, false);
break;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
exifValue.Value = value;
return exifValue;
} }
/// <summary> /// <summary>
@ -635,29 +581,35 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// Returns an EXIF value with a numeric type for the given tag. /// Returns an EXIF value with a numeric type for the given tag.
/// </summary> /// </summary>
/// <param name="tag">The tag.</param> /// <param name="tag">The tag.</param>
/// <param name="type">The numeric type.</param> /// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param> /// <param name="isArray">Whether the value is an array.</param>
/// <returns> /// <returns>
/// The <see cref="ExifValue"/>. /// The <see cref="ExifValue"/>.
/// </returns> /// </returns>
private static ExifValue CreateNumber(ExifTag tag, Type type, bool isArray) private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray)
{ {
Type type = value?.GetType();
if (type != null && type.IsArray)
{
type = type.GetElementType();
}
if (type == null || type == typeof(ushort)) if (type == null || type == typeof(ushort))
{ {
return new ExifValue(tag, ExifDataType.Short, isArray); return new ExifValue(tag, ExifDataType.Short, value, isArray);
} }
if (type == typeof(short)) if (type == typeof(short))
{ {
return new ExifValue(tag, ExifDataType.SignedShort, isArray); return new ExifValue(tag, ExifDataType.SignedShort, value, isArray);
} }
if (type == typeof(uint)) if (type == typeof(uint))
{ {
return new ExifValue(tag, ExifDataType.Long, isArray); return new ExifValue(tag, ExifDataType.Long, value, isArray);
} }
return new ExifValue(tag, ExifDataType.SignedLong, isArray); return new ExifValue(tag, ExifDataType.SignedLong, value, isArray);
} }
/// <summary> /// <summary>
@ -739,8 +691,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <returns>The <see cref="string"/></returns> /// <returns>The <see cref="string"/></returns>
private string ToString(object value) private string ToString(object value)
{ {
string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, value); if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description)
if (description != null)
{ {
return description; return description;
} }
@ -788,7 +739,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private int GetHashCode(ExifValue exif) private int GetHashCode(ExifValue exif)
{ {
int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode(); int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode();
return hashCode ^ exif.exifValue?.GetHashCode() ?? hashCode; return hashCode ^ exif.Value?.GetHashCode() ?? hashCode;
} }
} }
} }

410
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -19,299 +19,28 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary> /// </summary>
private const int StartIndex = 6; private const int StartIndex = 6;
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
private static readonly ExifTag[] IfdTags =
{
ExifTag.SubfileType,
ExifTag.OldSubfileType,
ExifTag.ImageWidth,
ExifTag.ImageLength,
ExifTag.BitsPerSample,
ExifTag.Compression,
ExifTag.PhotometricInterpretation,
ExifTag.Thresholding,
ExifTag.CellWidth,
ExifTag.CellLength,
ExifTag.FillOrder,
ExifTag.DocumentName,
ExifTag.ImageDescription,
ExifTag.Make,
ExifTag.Model,
ExifTag.StripOffsets,
ExifTag.Orientation,
ExifTag.SamplesPerPixel,
ExifTag.RowsPerStrip,
ExifTag.StripByteCounts,
ExifTag.MinSampleValue,
ExifTag.MaxSampleValue,
ExifTag.XResolution,
ExifTag.YResolution,
ExifTag.PlanarConfiguration,
ExifTag.PageName,
ExifTag.XPosition,
ExifTag.YPosition,
ExifTag.FreeOffsets,
ExifTag.FreeByteCounts,
ExifTag.GrayResponseUnit,
ExifTag.GrayResponseCurve,
ExifTag.T4Options,
ExifTag.T6Options,
ExifTag.ResolutionUnit,
ExifTag.PageNumber,
ExifTag.ColorResponseUnit,
ExifTag.TransferFunction,
ExifTag.Software,
ExifTag.DateTime,
ExifTag.Artist,
ExifTag.HostComputer,
ExifTag.Predictor,
ExifTag.WhitePoint,
ExifTag.PrimaryChromaticities,
ExifTag.ColorMap,
ExifTag.HalftoneHints,
ExifTag.TileWidth,
ExifTag.TileLength,
ExifTag.TileOffsets,
ExifTag.TileByteCounts,
ExifTag.BadFaxLines,
ExifTag.CleanFaxData,
ExifTag.ConsecutiveBadFaxLines,
ExifTag.InkSet,
ExifTag.InkNames,
ExifTag.NumberOfInks,
ExifTag.DotRange,
ExifTag.TargetPrinter,
ExifTag.ExtraSamples,
ExifTag.SampleFormat,
ExifTag.SMinSampleValue,
ExifTag.SMaxSampleValue,
ExifTag.TransferRange,
ExifTag.ClipPath,
ExifTag.XClipPathUnits,
ExifTag.YClipPathUnits,
ExifTag.Indexed,
ExifTag.JPEGTables,
ExifTag.OPIProxy,
ExifTag.ProfileType,
ExifTag.FaxProfile,
ExifTag.CodingMethods,
ExifTag.VersionYear,
ExifTag.ModeNumber,
ExifTag.Decode,
ExifTag.DefaultImageColor,
ExifTag.T82ptions,
ExifTag.JPEGProc,
ExifTag.JPEGInterchangeFormat,
ExifTag.JPEGInterchangeFormatLength,
ExifTag.JPEGRestartInterval,
ExifTag.JPEGLosslessPredictors,
ExifTag.JPEGPointTransforms,
ExifTag.JPEGQTables,
ExifTag.JPEGDCTables,
ExifTag.JPEGACTables,
ExifTag.YCbCrCoefficients,
ExifTag.YCbCrSubsampling,
ExifTag.YCbCrSubsampling,
ExifTag.YCbCrPositioning,
ExifTag.ReferenceBlackWhite,
ExifTag.StripRowCounts,
ExifTag.XMP,
ExifTag.Rating,
ExifTag.RatingPercent,
ExifTag.ImageID,
ExifTag.CFARepeatPatternDim,
ExifTag.CFAPattern2,
ExifTag.BatteryLevel,
ExifTag.Copyright,
ExifTag.MDFileTag,
ExifTag.MDScalePixel,
ExifTag.MDLabName,
ExifTag.MDSampleInfo,
ExifTag.MDPrepDate,
ExifTag.MDPrepTime,
ExifTag.MDFileUnits,
ExifTag.PixelScale,
ExifTag.IntergraphPacketData,
ExifTag.IntergraphRegisters,
ExifTag.IntergraphMatrix,
ExifTag.ModelTiePoint,
ExifTag.SEMInfo,
ExifTag.ModelTransform,
ExifTag.ImageLayer,
ExifTag.FaxRecvParams,
ExifTag.FaxSubaddress,
ExifTag.FaxRecvTime,
ExifTag.ImageSourceData,
ExifTag.XPTitle,
ExifTag.XPComment,
ExifTag.XPAuthor,
ExifTag.XPKeywords,
ExifTag.XPSubject,
ExifTag.GDALMetadata,
ExifTag.GDALNoData
};
/// <summary>
/// The collection of Exif tags
/// </summary>
private static readonly ExifTag[] ExifTags =
{
ExifTag.ExposureTime,
ExifTag.FNumber,
ExifTag.ExposureProgram,
ExifTag.SpectralSensitivity,
ExifTag.ISOSpeedRatings,
ExifTag.OECF,
ExifTag.Interlace,
ExifTag.TimeZoneOffset,
ExifTag.SelfTimerMode,
ExifTag.SensitivityType,
ExifTag.StandardOutputSensitivity,
ExifTag.RecommendedExposureIndex,
ExifTag.ISOSpeed,
ExifTag.ISOSpeedLatitudeyyy,
ExifTag.ISOSpeedLatitudezzz,
ExifTag.ExifVersion,
ExifTag.DateTimeOriginal,
ExifTag.DateTimeDigitized,
ExifTag.OffsetTime,
ExifTag.OffsetTimeOriginal,
ExifTag.OffsetTimeDigitized,
ExifTag.ComponentsConfiguration,
ExifTag.CompressedBitsPerPixel,
ExifTag.ShutterSpeedValue,
ExifTag.ApertureValue,
ExifTag.BrightnessValue,
ExifTag.ExposureBiasValue,
ExifTag.MaxApertureValue,
ExifTag.SubjectDistance,
ExifTag.MeteringMode,
ExifTag.LightSource,
ExifTag.Flash,
ExifTag.FocalLength,
ExifTag.FlashEnergy2,
ExifTag.SpatialFrequencyResponse2,
ExifTag.Noise,
ExifTag.FocalPlaneXResolution2,
ExifTag.FocalPlaneYResolution2,
ExifTag.FocalPlaneResolutionUnit2,
ExifTag.ImageNumber,
ExifTag.SecurityClassification,
ExifTag.ImageHistory,
ExifTag.SubjectArea,
ExifTag.ExposureIndex2,
ExifTag.TIFFEPStandardID,
ExifTag.SensingMethod2,
ExifTag.MakerNote,
ExifTag.UserComment,
ExifTag.SubsecTime,
ExifTag.SubsecTimeOriginal,
ExifTag.SubsecTimeDigitized,
ExifTag.AmbientTemperature,
ExifTag.Humidity,
ExifTag.Pressure,
ExifTag.WaterDepth,
ExifTag.Acceleration,
ExifTag.CameraElevationAngle,
ExifTag.FlashpixVersion,
ExifTag.ColorSpace,
ExifTag.PixelXDimension,
ExifTag.PixelYDimension,
ExifTag.RelatedSoundFile,
ExifTag.FlashEnergy,
ExifTag.SpatialFrequencyResponse,
ExifTag.FocalPlaneXResolution,
ExifTag.FocalPlaneYResolution,
ExifTag.FocalPlaneResolutionUnit,
ExifTag.SubjectLocation,
ExifTag.ExposureIndex,
ExifTag.SensingMethod,
ExifTag.FileSource,
ExifTag.SceneType,
ExifTag.CFAPattern,
ExifTag.CustomRendered,
ExifTag.ExposureMode,
ExifTag.WhiteBalance,
ExifTag.DigitalZoomRatio,
ExifTag.FocalLengthIn35mmFilm,
ExifTag.SceneCaptureType,
ExifTag.GainControl,
ExifTag.Contrast,
ExifTag.Saturation,
ExifTag.Sharpness,
ExifTag.DeviceSettingDescription,
ExifTag.SubjectDistanceRange,
ExifTag.ImageUniqueID,
ExifTag.OwnerName,
ExifTag.SerialNumber,
ExifTag.LensInfo,
ExifTag.LensMake,
ExifTag.LensModel,
ExifTag.LensSerialNumber
};
/// <summary>
/// The collection of GPS tags
/// </summary>
private static readonly ExifTag[] GPSTags =
{
ExifTag.GPSVersionID,
ExifTag.GPSLatitudeRef,
ExifTag.GPSLatitude,
ExifTag.GPSLongitudeRef,
ExifTag.GPSLongitude,
ExifTag.GPSAltitudeRef,
ExifTag.GPSAltitude,
ExifTag.GPSTimestamp,
ExifTag.GPSSatellites,
ExifTag.GPSStatus,
ExifTag.GPSMeasureMode,
ExifTag.GPSDOP,
ExifTag.GPSSpeedRef,
ExifTag.GPSSpeed,
ExifTag.GPSTrackRef,
ExifTag.GPSTrack,
ExifTag.GPSImgDirectionRef,
ExifTag.GPSImgDirection,
ExifTag.GPSMapDatum,
ExifTag.GPSDestLatitudeRef,
ExifTag.GPSDestLatitude,
ExifTag.GPSDestLongitudeRef,
ExifTag.GPSDestLongitude,
ExifTag.GPSDestBearingRef,
ExifTag.GPSDestBearing,
ExifTag.GPSDestDistanceRef,
ExifTag.GPSDestDistance,
ExifTag.GPSProcessingMethod,
ExifTag.GPSAreaInformation,
ExifTag.GPSDateStamp,
ExifTag.GPSDifferential
};
/// <summary> /// <summary>
/// Which parts will be written. /// Which parts will be written.
/// </summary> /// </summary>
private ExifParts allowedParts; private ExifParts allowedParts;
private Collection<ExifValue> values; private IList<ExifValue> values;
private Collection<int> dataOffsets; private List<int> dataOffsets;
private Collection<int> ifdIndexes; private List<int> ifdIndexes;
private Collection<int> exifIndexes; private List<int> exifIndexes;
private Collection<int> gpsIndexes; private List<int> gpsIndexes;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExifWriter"/> class. /// Initializes a new instance of the <see cref="ExifWriter"/> class.
/// </summary> /// </summary>
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
/// <param name="allowedParts">The allowed parts.</param> /// <param name="allowedParts">The allowed parts.</param>
public ExifWriter(Collection<ExifValue> values, ExifParts allowedParts) public ExifWriter(IList<ExifValue> values, ExifParts allowedParts)
{ {
this.values = values; this.values = values;
this.allowedParts = allowedParts; this.allowedParts = allowedParts;
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, IfdTags); this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags); this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, GPSTags); this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps);
} }
/// <summary> /// <summary>
@ -360,6 +89,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
length += 10 + 4 + 2; length += 10 + 4 + 2;
byte[] result = new byte[length]; byte[] result = new byte[length];
result[0] = (byte)'E'; result[0] = (byte)'E';
result[1] = (byte)'x'; result[1] = (byte)'x';
result[2] = (byte)'i'; result[2] = (byte)'i';
@ -377,17 +107,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (exifLength > 0) if (exifLength > 0)
{ {
this.values[exifIndex].Value = ifdOffset + ifdLength; this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength);
} }
if (gpsLength > 0) if (gpsLength > 0)
{ {
this.values[gpsIndex].Value = ifdOffset + ifdLength + exifLength; this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength);
} }
i = Write(BitConverter.GetBytes(ifdOffset), result, i); i = WriteUInt32(ifdOffset, result, i);
i = this.WriteHeaders(this.ifdIndexes, result, i); i = this.WriteHeaders(this.ifdIndexes, result, i);
i = Write(BitConverter.GetBytes(thumbnailOffset), result, i); i = WriteUInt32(thumbnailOffset, result, i);
i = this.WriteData(this.ifdIndexes, result, i); i = this.WriteData(this.ifdIndexes, result, i);
if (exifLength > 0) if (exifLength > 0)
@ -402,19 +132,61 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
i = this.WriteData(this.gpsIndexes, result, i); i = this.WriteData(this.gpsIndexes, result, i);
} }
Write(BitConverter.GetBytes((ushort)0), result, i); WriteUInt16((ushort)0, result, i);
return result; return result;
} }
private static int Write(byte[] source, byte[] destination, int offset) private static unsafe int WriteSingle(float value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), *((int*)&value));
return offset + 4;
}
private static unsafe int WriteDouble(double value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), *((long*)&value));
return offset + 8;
}
private static int Write(ReadOnlySpan<byte> source, Span<byte> destination, int offset)
{ {
Buffer.BlockCopy(source, 0, destination, offset, source.Length); source.CopyTo(destination.Slice(offset, source.Length));
return offset + source.Length; return offset + source.Length;
} }
private int GetIndex(Collection<int> indexes, ExifTag tag) private static int WriteInt16(short value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt16LittleEndian(destination.Slice(offset, 2), value);
return offset + 2;
}
private static int WriteUInt16(ushort value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(offset, 2), value);
return offset + 2;
}
private static int WriteUInt32(uint value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(offset, 4), value);
return offset + 4;
}
private static int WriteInt32(int value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value);
return offset + 4;
}
private int GetIndex(IList<int> indexes, ExifTag tag)
{ {
foreach (int index in indexes) foreach (int index in indexes)
{ {
@ -430,14 +202,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newIndex; return newIndex;
} }
private Collection<int> GetIndexes(ExifParts part, ExifTag[] tags) private List<int> GetIndexes(ExifParts part, ExifTag[] tags)
{ {
if (((int)this.allowedParts & (int)part) == 0) if (((int)this.allowedParts & (int)part) == 0)
{ {
return new Collection<int>(); return new List<int>();
} }
Collection<int> result = new Collection<int>(); var result = new List<int>();
for (int i = 0; i < this.values.Count; i++) for (int i = 0; i < this.values.Count; i++)
{ {
ExifValue value = this.values[i]; ExifValue value = this.values[i];
@ -457,7 +229,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result; return result;
} }
private uint GetLength(IEnumerable<int> indexes) private uint GetLength(IList<int> indexes)
{ {
uint length = 0; uint length = 0;
@ -494,7 +266,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset; return newOffset;
} }
private int WriteData(Collection<int> indexes, byte[] destination, int offset) private int WriteData(List<int> indexes, byte[] destination, int offset)
{ {
if (this.dataOffsets.Count == 0) if (this.dataOffsets.Count == 0)
{ {
@ -509,7 +281,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
ExifValue value = this.values[index]; ExifValue value = this.values[index];
if (value.Length > 4) if (value.Length > 4)
{ {
Write(BitConverter.GetBytes(newOffset - StartIndex), destination, this.dataOffsets[i++]); WriteUInt32((uint)(newOffset - StartIndex), destination, this.dataOffsets[i++]);
newOffset = this.WriteValue(value, destination, newOffset); newOffset = this.WriteValue(value, destination, newOffset);
} }
} }
@ -517,11 +289,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset; return newOffset;
} }
private int WriteHeaders(Collection<int> indexes, byte[] destination, int offset) private int WriteHeaders(List<int> indexes, byte[] destination, int offset)
{ {
this.dataOffsets = new Collection<int>(); this.dataOffsets = new List<int>();
int newOffset = Write(BitConverter.GetBytes((ushort)indexes.Count), destination, offset); int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset);
if (indexes.Count == 0) if (indexes.Count == 0)
{ {
@ -531,9 +303,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
foreach (int index in indexes) foreach (int index in indexes)
{ {
ExifValue value = this.values[index]; ExifValue value = this.values[index];
newOffset = Write(BitConverter.GetBytes((ushort)value.Tag), destination, newOffset); newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset);
newOffset = Write(BitConverter.GetBytes((ushort)value.DataType), destination, newOffset); newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset);
newOffset = Write(BitConverter.GetBytes((uint)value.NumberOfComponents), destination, newOffset); newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset);
if (value.Length > 4) if (value.Length > 4)
{ {
@ -550,23 +322,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset; return newOffset;
} }
private int WriteRational(Rational value, byte[] destination, int offset) private static void WriteRational(Span<byte> destination, in Rational value)
{ {
Write(BitConverter.GetBytes(value.Numerator), destination, offset); BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4); BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
return offset + 8;
} }
private int WriteSignedRational(SignedRational value, byte[] destination, int offset) private static void WriteSignedRational(Span<byte> destination, in SignedRational value)
{ {
Write(BitConverter.GetBytes(value.Numerator), destination, offset); BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4); BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
return offset + 8;
} }
private int WriteValue(ExifDataType dataType, object value, byte[] destination, int offset) private int WriteValue(ExifDataType dataType, object value, Span<byte> destination, int offset)
{ {
switch (dataType) switch (dataType)
{ {
@ -577,24 +345,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
destination[offset] = (byte)value; destination[offset] = (byte)value;
return offset + 1; return offset + 1;
case ExifDataType.DoubleFloat: case ExifDataType.DoubleFloat:
return Write(BitConverter.GetBytes((double)value), destination, offset); return WriteDouble((double)value, destination, offset);
case ExifDataType.Short: case ExifDataType.Short:
return Write(BitConverter.GetBytes((ushort)value), destination, offset); return WriteUInt16((ushort)value, destination, offset);
case ExifDataType.Long: case ExifDataType.Long:
return Write(BitConverter.GetBytes((uint)value), destination, offset); return WriteUInt32((uint)value, destination, offset);
case ExifDataType.Rational: case ExifDataType.Rational:
return this.WriteRational((Rational)value, destination, offset); WriteRational(destination.Slice(offset, 8), (Rational)value);
return offset + 8;
case ExifDataType.SignedByte: case ExifDataType.SignedByte:
destination[offset] = unchecked((byte)((sbyte)value)); destination[offset] = unchecked((byte)((sbyte)value));
return offset + 1; return offset + 1;
case ExifDataType.SignedLong: case ExifDataType.SignedLong:
return Write(BitConverter.GetBytes((int)value), destination, offset); return WriteInt32((int)value, destination, offset);
case ExifDataType.SignedShort: case ExifDataType.SignedShort:
return Write(BitConverter.GetBytes((short)value), destination, offset); return WriteInt16((short)value, destination, offset);
case ExifDataType.SignedRational: case ExifDataType.SignedRational:
return this.WriteSignedRational((SignedRational)value, destination, offset); WriteSignedRational(destination.Slice(offset, 8), (SignedRational)value);
return offset + 8;
case ExifDataType.SingleFloat: case ExifDataType.SingleFloat:
return Write(BitConverter.GetBytes((float)value), destination, offset); return WriteSingle((float)value, destination, offset);
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
} }

2
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs

@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return true; return true;
} }
return obj is IccParametricCurve && this.Equals((IccParametricCurve)obj); return obj is IccParametricCurve other && this.Equals(other);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs

@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return true; return true;
} }
return obj is IccResponseCurve && this.Equals((IccResponseCurve)obj); return obj is IccResponseCurve other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/Alpha8.cs

@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <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 bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is Alpha8) && this.Equals((Alpha8)obj); return obj is Alpha8 other && this.Equals(other);
} }
/// <summary> /// <summary>

2
src/ImageSharp/PixelFormats/Bgr24.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj?.GetType() == typeof(Bgr24) && this.Equals((Bgr24)obj); return obj is Bgr24 other && this.Equals(other);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/PixelFormats/Bgra32.cs

@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.PixelFormats
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) => obj?.GetType() == typeof(Bgra32) && this.Equals((Bgra32)obj); public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other);
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()

2
src/ImageSharp/PixelFormats/Bgra4444.cs

@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is Bgra4444) && this.Equals((Bgra4444)obj); return obj is Bgra4444 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is Bgra5551) && this.Equals((Bgra5551)obj); return obj is Bgra5551 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

6
src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs

@ -32,8 +32,8 @@ namespace SixLabors.ImageSharp.PixelFormats
throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
} }
TPixel result = default(TPixel); TPixel result = default;
Rgba32 rgba = new Rgba32( var rgba = new Rgba32(
(byte)(packedValue >> 24), (byte)(packedValue >> 24),
(byte)(packedValue >> 16), (byte)(packedValue >> 16),
(byte)(packedValue >> 8), (byte)(packedValue >> 8),
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA values.</returns> /// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA values.</returns>
public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha) public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha)
{ {
TPixel color = default(TPixel); TPixel color = default;
color.PackFromRgba32(new Rgba32(red, green, blue, alpha)); color.PackFromRgba32(new Rgba32(red, green, blue, alpha));
return color; return color;
} }

2
src/ImageSharp/PixelFormats/HalfVector4.cs

@ -191,7 +191,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is HalfVector4) && this.Equals((HalfVector4)obj); return obj is HalfVector4 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs

@ -735,7 +735,7 @@ namespace SixLabors.ImageSharp.PixelFormats
private static TPixel[] GetWebSafePalette() private static TPixel[] GetWebSafePalette()
{ {
Rgba32[] constants = ColorConstants.WebSafeColors; Rgba32[] constants = ColorConstants.WebSafeColors;
TPixel[] safe = new TPixel[constants.Length + 1]; var safe = new TPixel[constants.Length + 1];
Span<byte> constantsBytes = constants.AsSpan().NonPortableCast<Rgba32, byte>(); Span<byte> constantsBytes = constants.AsSpan().NonPortableCast<Rgba32, byte>();
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length); PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length);

2
src/ImageSharp/PixelFormats/NormalizedByte2.cs

@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is NormalizedByte2) && this.Equals((NormalizedByte2)obj); return obj is NormalizedByte2 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/NormalizedByte4.cs

@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is NormalizedByte4) && this.Equals((NormalizedByte4)obj); return obj is NormalizedByte4 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/NormalizedShort4.cs

@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is NormalizedShort4) && this.Equals((NormalizedShort4)obj); return obj is NormalizedShort4 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/Rgb24.cs

@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj?.GetType() == typeof(Rgb24) && this.Equals((Rgb24)obj); return obj is Rgb24 other && this.Equals(other);
} }
/// <inheritdoc/> /// <inheritdoc/>

1
src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {

2
src/ImageSharp/PixelFormats/RgbaVector.cs

@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is RgbaVector) && this.Equals((RgbaVector)obj); return obj is RgbaVector other && this.Equals(other);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/PixelFormats/Short2.cs

@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is Short2) && this.Equals((Short2)obj); return obj is Short2 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/Short4.cs

@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is Short4) && this == (Short4)obj; return obj is Short4 other && this.Equals(other);
} }
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/Primitives/DenseMatrix{T}.cs

@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Primitives
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) => obj is DenseMatrix<T> matrix && this.Equals(matrix); public override bool Equals(object obj) => obj is DenseMatrix<T> other && this.Equals(other);
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() => this.Data.GetHashCode(); public override int GetHashCode() => this.Data.GetHashCode();

275
src/ImageSharp/Primitives/LongRational.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <remarks> /// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only. /// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks> /// </remarks>
internal struct LongRational : IEquatable<LongRational> internal readonly struct LongRational : IEquatable<LongRational>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LongRational"/> struct. /// Initializes a new instance of the <see cref="LongRational"/> struct.
@ -26,126 +26,25 @@ namespace SixLabors.ImageSharp.Primitives
/// The number below the line in a vulgar fraction; a divisor. /// The number below the line in a vulgar fraction; a divisor.
/// </param> /// </param>
public LongRational(long numerator, long denominator) public LongRational(long numerator, long denominator)
: this(numerator, denominator, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LongRational"/> struct.
/// </summary>
/// <param name="numerator">
/// The number above the line in a vulgar fraction showing how many of the parts
/// indicated by the denominator are taken.
/// </param>
/// <param name="denominator">
/// The number below the line in a vulgar fraction; a divisor.
/// </param>
/// <param name="simplify">
/// Whether to attempt to simplify the fractional parts.
/// </param>
public LongRational(long numerator, long denominator, bool simplify)
: this()
{ {
this.Numerator = numerator; this.Numerator = numerator;
this.Denominator = denominator; this.Denominator = denominator;
if (simplify)
{
this.Simplify();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="LongRational"/> struct.
/// </summary>
/// <param name="value">The <see cref="double"/> to create the instance from.</param>
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public LongRational(double value, bool bestPrecision)
: this()
{
if (double.IsNaN(value))
{
this.Numerator = this.Denominator = 0;
return;
}
if (double.IsPositiveInfinity(value))
{
this.Numerator = 1;
this.Denominator = 0;
return;
}
if (double.IsNegativeInfinity(value))
{
this.Numerator = -1;
this.Denominator = 0;
return;
}
this.Numerator = 1;
this.Denominator = 1;
double val = Math.Abs(value);
double df = this.Numerator / (double)this.Denominator;
double epsilon = bestPrecision ? double.Epsilon : .000001;
while (Math.Abs(df - val) > epsilon)
{
if (df < val)
{
this.Numerator++;
}
else
{
this.Denominator++;
this.Numerator = (int)(val * this.Denominator);
}
df = this.Numerator / (double)this.Denominator;
}
if (value < 0.0)
{
this.Numerator *= -1;
}
this.Simplify();
} }
/// <summary> /// <summary>
/// Gets the numerator of a number. /// Gets the numerator of a number.
/// </summary> /// </summary>
public long Numerator public long Numerator { get; }
{
get;
private set;
}
/// <summary> /// <summary>
/// Gets the denominator of a number. /// Gets the denominator of a number.
/// </summary> /// </summary>
public long Denominator public long Denominator { get; }
{
get;
private set;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is indeterminate. /// Gets a value indicating whether this instance is indeterminate.
/// </summary> /// </summary>
public bool IsIndeterminate public bool IsIndeterminate => this.Denominator == 0 && this.Numerator == 0;
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == 0;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is an integer (n, 1) /// Gets a value indicating whether this instance is an integer (n, 1)
@ -155,76 +54,28 @@ namespace SixLabors.ImageSharp.Primitives
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to negative infinity (-1, 0) /// Gets a value indicating whether this instance is equal to negative infinity (-1, 0)
/// </summary> /// </summary>
public bool IsNegativeInfinity public bool IsNegativeInfinity => this.Denominator == 0 && this.Numerator == -1;
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == -1;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to positive infinity (1, 0) /// Gets a value indicating whether this instance is equal to positive infinity (1, 0)
/// </summary> /// </summary>
public bool IsPositiveInfinity public bool IsPositiveInfinity => this.Denominator == 0 && this.Numerator == 1;
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == 1;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to 0 (0, 1) /// Gets a value indicating whether this instance is equal to 0 (0, 1)
/// </summary> /// </summary>
public bool IsZero public bool IsZero => this.Denominator == 1 && this.Numerator == 0;
{
get
{
if (this.Denominator != 1)
{
return false;
}
return this.Numerator == 0;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(LongRational other) public bool Equals(LongRational other)
{ {
if (this.Denominator == other.Denominator) return this.Numerator == other.Numerator && this.Denominator == other.Denominator;
{
return this.Numerator == other.Numerator;
}
if (this.Numerator == 0 && this.Denominator == 0)
{
return other.Numerator == 0 && other.Denominator == 0;
}
if (other.Numerator == 0 && other.Denominator == 0)
{
return this.Numerator == 0 && this.Denominator == 0;
}
return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return this.GetHashCode(this); return ((this.Numerator * 397) ^ this.Denominator).GetHashCode();
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -268,86 +119,108 @@ namespace SixLabors.ImageSharp.Primitives
return this.Numerator.ToString(provider); return this.Numerator.ToString(provider);
} }
StringBuilder sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append(this.Numerator.ToString(provider)); sb.Append(this.Numerator.ToString(provider));
sb.Append("/"); sb.Append('/');
sb.Append(this.Denominator.ToString(provider)); sb.Append(this.Denominator.ToString(provider));
return sb.ToString(); return sb.ToString();
} }
/// <summary> /// <summary>
/// Finds the greatest common divisor of two <see cref="long"/> values. /// Create a new instance of the <see cref="LongRational"/> struct from a double value.
/// </summary> /// </summary>
/// <param name="left">The first value</param> /// <param name="value">The <see cref="double"/> to create the instance from.</param>
/// <param name="right">The second value</param> /// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
/// <returns>The <see cref="long"/></returns> public static LongRational FromDouble(double value, bool bestPrecision)
private static long GreatestCommonDivisor(long left, long right)
{ {
return right == 0 ? left : GreatestCommonDivisor(right, left % right); if (double.IsNaN(value))
} {
return new LongRational(0, 0);
}
/// <summary> if (double.IsPositiveInfinity(value))
/// Simplifies the <see cref="LongRational"/>
/// </summary>
private void Simplify()
{
if (this.IsIndeterminate)
{ {
return; return new LongRational(1, 0);
} }
if (this.IsNegativeInfinity) if (double.IsNegativeInfinity(value))
{ {
return; return new LongRational(-1, 0);
} }
if (this.IsPositiveInfinity) long numerator = 1;
long denominator = 1;
double val = Math.Abs(value);
double df = numerator / (double)denominator;
double epsilon = bestPrecision ? double.Epsilon : .000001;
while (Math.Abs(df - val) > epsilon)
{ {
return; if (df < val)
{
numerator++;
}
else
{
denominator++;
numerator = (int)(val * denominator);
}
df = numerator / (double)denominator;
} }
if (this.IsInteger) if (value < 0.0)
{ {
return; numerator *= -1;
} }
if (this.IsZero) return new LongRational(numerator, denominator).Simplify();
}
/// <summary>
/// Finds the greatest common divisor of two <see cref="long"/> values.
/// </summary>
/// <param name="left">The first value</param>
/// <param name="right">The second value</param>
/// <returns>The <see cref="long"/></returns>
private static long GreatestCommonDivisor(long left, long right)
{
return right == 0 ? left : GreatestCommonDivisor(right, left % right);
}
/// <summary>
/// Simplifies the <see cref="LongRational"/>
/// </summary>
public LongRational Simplify()
{
if (this.IsIndeterminate ||
this.IsNegativeInfinity ||
this.IsPositiveInfinity ||
this.IsInteger ||
this.IsZero)
{ {
return; return this;
} }
if (this.Numerator == 0) if (this.Numerator == 0)
{ {
this.Denominator = 0; return new LongRational(0, 0);
return;
} }
if (this.Numerator == this.Denominator) if (this.Numerator == this.Denominator)
{ {
this.Numerator = 1; return new LongRational(1, 1);
this.Denominator = 1;
} }
long gcd = GreatestCommonDivisor(Math.Abs(this.Numerator), Math.Abs(this.Denominator)); long gcd = GreatestCommonDivisor(Math.Abs(this.Numerator), Math.Abs(this.Denominator));
if (gcd > 1) if (gcd > 1)
{ {
this.Numerator = this.Numerator / gcd; return new LongRational(this.Numerator / gcd, this.Denominator / gcd);
this.Denominator = this.Denominator / gcd;
} }
}
/// <summary> return this;
/// Returns the hash code for this instance.
/// </summary>
/// <param name="rational">
/// The instance of <see cref="LongRational"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(LongRational rational)
{
return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode();
} }
} }
} }

33
src/ImageSharp/Primitives/Rational.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <remarks> /// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only. /// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks> /// </remarks>
public struct Rational : IEquatable<Rational> public readonly struct Rational : IEquatable<Rational>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct. /// Initializes a new instance of the <see cref="Rational"/> struct.
@ -41,10 +41,18 @@ namespace SixLabors.ImageSharp.Primitives
/// <param name="simplify">Specified if the rational should be simplified.</param> /// <param name="simplify">Specified if the rational should be simplified.</param>
public Rational(uint numerator, uint denominator, bool simplify) public Rational(uint numerator, uint denominator, bool simplify)
{ {
LongRational rational = new LongRational(numerator, denominator, simplify); if (simplify)
{
LongRational rational = new LongRational(numerator, denominator).Simplify();
this.Numerator = (uint)rational.Numerator; this.Numerator = (uint)rational.Numerator;
this.Denominator = (uint)rational.Denominator; this.Denominator = (uint)rational.Denominator;
}
else
{
this.Numerator = numerator;
this.Denominator = denominator;
}
} }
/// <summary> /// <summary>
@ -63,7 +71,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param> /// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public Rational(double value, bool bestPrecision) public Rational(double value, bool bestPrecision)
{ {
LongRational rational = new LongRational(Math.Abs(value), bestPrecision); var rational = LongRational.FromDouble(Math.Abs(value), bestPrecision);
this.Numerator = (uint)rational.Numerator; this.Numerator = (uint)rational.Numerator;
this.Denominator = (uint)rational.Denominator; this.Denominator = (uint)rational.Denominator;
@ -129,19 +137,14 @@ namespace SixLabors.ImageSharp.Primitives
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Rational) return obj is Rational other && this.Equals(other);
{
return this.Equals((Rational)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(Rational other) public bool Equals(Rational other)
{ {
LongRational left = new LongRational(this.Numerator, this.Denominator); var left = new LongRational(this.Numerator, this.Denominator);
LongRational right = new LongRational(other.Numerator, other.Denominator); var right = new LongRational(other.Numerator, other.Denominator);
return left.Equals(right); return left.Equals(right);
} }
@ -149,7 +152,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
LongRational self = new LongRational(this.Numerator, this.Denominator); var self = new LongRational(this.Numerator, this.Denominator);
return self.GetHashCode(); return self.GetHashCode();
} }
@ -180,7 +183,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <returns>The <see cref="string"/></returns> /// <returns>The <see cref="string"/></returns>
public string ToString(IFormatProvider provider) public string ToString(IFormatProvider provider)
{ {
LongRational rational = new LongRational(this.Numerator, this.Denominator); var rational = new LongRational(this.Numerator, this.Denominator);
return rational.ToString(provider); return rational.ToString(provider);
} }
} }

33
src/ImageSharp/Primitives/SignedRational.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <remarks> /// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only. /// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks> /// </remarks>
public struct SignedRational : IEquatable<SignedRational> public readonly struct SignedRational : IEquatable<SignedRational>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct. /// Initializes a new instance of the <see cref="SignedRational"/> struct.
@ -41,10 +41,18 @@ namespace SixLabors.ImageSharp.Primitives
/// <param name="simplify">Specified if the rational should be simplified.</param> /// <param name="simplify">Specified if the rational should be simplified.</param>
public SignedRational(int numerator, int denominator, bool simplify) public SignedRational(int numerator, int denominator, bool simplify)
{ {
LongRational rational = new LongRational(numerator, denominator, simplify); if (simplify)
{
LongRational rational = new LongRational(numerator, denominator).Simplify();
this.Numerator = (int)rational.Numerator; this.Numerator = (int)rational.Numerator;
this.Denominator = (int)rational.Denominator; this.Denominator = (int)rational.Denominator;
}
else
{
this.Numerator = numerator;
this.Denominator = denominator;
}
} }
/// <summary> /// <summary>
@ -63,7 +71,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param> /// <param name="bestPrecision">Whether to use the best possible precision when parsing the value.</param>
public SignedRational(double value, bool bestPrecision) public SignedRational(double value, bool bestPrecision)
{ {
LongRational rational = new LongRational(value, bestPrecision); var rational = LongRational.FromDouble(value, bestPrecision);
this.Numerator = (int)rational.Numerator; this.Numerator = (int)rational.Numerator;
this.Denominator = (int)rational.Denominator; this.Denominator = (int)rational.Denominator;
@ -129,19 +137,14 @@ namespace SixLabors.ImageSharp.Primitives
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is SignedRational) return obj is SignedRational other && this.Equals(other);
{
return this.Equals((SignedRational)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(SignedRational other) public bool Equals(SignedRational other)
{ {
LongRational left = new LongRational(this.Numerator, this.Denominator); var left = new LongRational(this.Numerator, this.Denominator);
LongRational right = new LongRational(other.Numerator, other.Denominator); var right = new LongRational(other.Numerator, other.Denominator);
return left.Equals(right); return left.Equals(right);
} }
@ -149,7 +152,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
LongRational self = new LongRational(this.Numerator, this.Denominator); var self = new LongRational(this.Numerator, this.Denominator);
return self.GetHashCode(); return self.GetHashCode();
} }
@ -180,7 +183,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <returns>The <see cref="string"/></returns> /// <returns>The <see cref="string"/></returns>
public string ToString(IFormatProvider provider) public string ToString(IFormatProvider provider)
{ {
LongRational rational = new LongRational(this.Numerator, this.Denominator); var rational = new LongRational(this.Numerator, this.Denominator);
return rational.ToString(provider); return rational.ToString(provider);
} }
} }

16
tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs

@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var apSource = new Span<TestStructs.Foo>(source, 1, source.Length - 1); var apSource = new Span<TestStructs.Foo>(source, 1, source.Length - 1);
var apDest = new Span<TestStructs.Foo>(dest, 1, dest.Length - 1); var apDest = new Span<TestStructs.Foo>(dest, 1, dest.Length - 1);
SpanHelper.Copy(apSource, apDest, count - 1); apSource.Slice(0, count - 1).CopyTo(apDest);
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
AssertNotDefault(dest, 1); AssertNotDefault(dest, 1);
@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var apSource = new Span<TestStructs.AlignedFoo>(source, 1, source.Length - 1); var apSource = new Span<TestStructs.AlignedFoo>(source, 1, source.Length - 1);
var apDest = new Span<TestStructs.AlignedFoo>(dest, 1, dest.Length - 1); var apDest = new Span<TestStructs.AlignedFoo>(dest, 1, dest.Length - 1);
SpanHelper.Copy(apSource, apDest, count - 1); apSource.Slice(0, count - 1).CopyTo(apDest);
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
AssertNotDefault(dest, 1); AssertNotDefault(dest, 1);
@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var apSource = new Span<int>(source, 1, source.Length - 1); var apSource = new Span<int>(source, 1, source.Length - 1);
var apDest = new Span<int>(dest, 1, dest.Length - 1); var apDest = new Span<int>(dest, 1, dest.Length - 1);
SpanHelper.Copy(apSource, apDest, count - 1); apSource.Slice(0, count - 1).CopyTo(apDest);
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
AssertNotDefault(dest, 1); AssertNotDefault(dest, 1);
@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var apSource = new Span<TestStructs.Foo>(source, 1, source.Length - 1); var apSource = new Span<TestStructs.Foo>(source, 1, source.Length - 1);
var apDest = new Span<byte>(dest, sizeof(TestStructs.Foo), dest.Length - sizeof(TestStructs.Foo)); var apDest = new Span<byte>(dest, sizeof(TestStructs.Foo), dest.Length - sizeof(TestStructs.Foo));
SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.Foo)); apSource.AsBytes().Slice(0, (count - 1) * sizeof(TestStructs.Foo)).CopyTo(apDest);
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var apSource = new Span<TestStructs.AlignedFoo>(source, 1, source.Length - 1); var apSource = new Span<TestStructs.AlignedFoo>(source, 1, source.Length - 1);
var apDest = new Span<byte>(dest, sizeof(TestStructs.AlignedFoo), dest.Length - sizeof(TestStructs.AlignedFoo)); var apDest = new Span<byte>(dest, sizeof(TestStructs.AlignedFoo), dest.Length - sizeof(TestStructs.AlignedFoo));
SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.AlignedFoo)); apSource.AsBytes().Slice(0, (count - 1) * sizeof(TestStructs.AlignedFoo)).CopyTo(apDest);
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var apSource = new Span<int>(source); var apSource = new Span<int>(source);
var apDest = new Span<byte>(dest); var apDest = new Span<byte>(dest);
SpanHelper.Copy(apSource.AsBytes(), apDest, count * sizeof(int)); apSource.AsBytes().Slice(0, count * sizeof(int)).CopyTo(apDest);
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -198,12 +198,12 @@ namespace SixLabors.ImageSharp.Tests.Memory
{ {
int srcCount = count * sizeof(TestStructs.Foo); int srcCount = count * sizeof(TestStructs.Foo);
byte[] source = CreateTestBytes(srcCount); byte[] source = CreateTestBytes(srcCount);
TestStructs.Foo[] dest = new TestStructs.Foo[count + 2]; var dest = new TestStructs.Foo[count + 2];
var apSource = new Span<byte>(source); var apSource = new Span<byte>(source);
var apDest = new Span<TestStructs.Foo>(dest); var apDest = new Span<TestStructs.Foo>(dest);
SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(TestStructs.Foo)); apSource.Slice(0, count * sizeof(TestStructs.Foo)).CopyTo(apDest.AsBytes());
AssertNotDefault(source, sizeof(TestStructs.Foo) + 1); AssertNotDefault(source, sizeof(TestStructs.Foo) + 1);
AssertNotDefault(dest, 1); AssertNotDefault(dest, 1);

18
tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs

@ -18,13 +18,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void AreEqual() public void AreEqual()
{ {
ImageProperty property1 = new ImageProperty("Foo", "Bar"); var property1 = new ImageProperty("Foo", "Bar");
ImageProperty property2 = new ImageProperty("Foo", "Bar"); var property2 = new ImageProperty("Foo", "Bar");
ImageProperty property3 = null;
Assert.Equal(property1, property2); Assert.Equal(property1, property2);
Assert.True(property1 == property2); Assert.True(property1 == property2);
Assert.Null(property3);
} }
/// <summary> /// <summary>
@ -33,15 +31,13 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void AreNotEqual() public void AreNotEqual()
{ {
ImageProperty property1 = new ImageProperty("Foo", "Bar"); var property1 = new ImageProperty("Foo", "Bar");
ImageProperty property2 = new ImageProperty("Foo", "Foo"); var property2 = new ImageProperty("Foo", "Foo");
ImageProperty property3 = new ImageProperty("Bar", "Bar"); var property3 = new ImageProperty("Bar", "Bar");
ImageProperty property4 = new ImageProperty("Foo", null); var property4 = new ImageProperty("Foo", null);
Assert.False(property1.Equals("Foo")); Assert.False(property1.Equals("Foo"));
Assert.NotNull(property1);
Assert.NotEqual(property1, property2); Assert.NotEqual(property1, property2);
Assert.True(property1 != property2); Assert.True(property1 != property2);
@ -66,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void ConstructorAssignsProperties() public void ConstructorAssignsProperties()
{ {
ImageProperty property = new ImageProperty("Foo", null); var property = new ImageProperty("Foo", null);
Assert.Equal("Foo", property.Name); Assert.Equal("Foo", property.Name);
Assert.Null(property.Value); Assert.Null(property.Value);

19
tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs

@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests
ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.Software); ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.Software);
TestValue(value, "ImageSharp"); TestValue(value, "ImageSharp");
Assert.Throws<ArgumentException>(() => { value.Value = 15; }); Assert.Throws<ArgumentException>(() => { value.WithValue(15); });
image.MetaData.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); image.MetaData.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55));
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests
TestValue(value, new SignedRational(7555, 100)); TestValue(value, new SignedRational(7555, 100));
Assert.Throws<ArgumentException>(() => { value.Value = 75; }); Assert.Throws<ArgumentException>(() => { value.WithValue(75); });
image.MetaData.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); image.MetaData.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0));
@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests
value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
TestValue(value, new Rational(150, 1)); TestValue(value, new Rational(150, 1));
Assert.Throws<ArgumentException>(() => { value.Value = "ImageSharp"; }); Assert.Throws<ArgumentException>(() => { value.WithValue("ImageSharp"); });
image.MetaData.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); image.MetaData.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null);
@ -209,11 +209,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void Syncs() public void Syncs()
{ {
ExifProfile exifProfile = new ExifProfile(); var exifProfile = new ExifProfile();
exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); exifProfile.SetValue(ExifTag.XResolution, new Rational(200));
exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); exifProfile.SetValue(ExifTag.YResolution, new Rational(300));
ImageMetaData metaData = new ImageMetaData(); var metaData = new ImageMetaData();
metaData.ExifProfile = exifProfile; metaData.ExifProfile = exifProfile;
metaData.HorizontalResolution = 200; metaData.HorizontalResolution = 200;
metaData.VerticalResolution = 300; metaData.VerticalResolution = 300;
@ -255,17 +255,17 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void WriteTooLargeProfile() public void WriteTooLargeProfile()
{ {
StringBuilder junk = new StringBuilder(); var junk = new StringBuilder();
for (int i = 0; i < 65500; i++) for (int i = 0; i < 65500; i++)
{ {
junk.Append("I"); junk.Append("I");
} }
Image<Rgba32> image = new Image<Rgba32>(100, 100); var image = new Image<Rgba32>(100, 100);
image.MetaData.ExifProfile = new ExifProfile(); image.MetaData.ExifProfile = new ExifProfile();
image.MetaData.ExifProfile.SetValue(ExifTag.ImageDescription, junk.ToString()); image.MetaData.ExifProfile.SetValue(ExifTag.ImageDescription, junk.ToString());
using (MemoryStream memStream = new MemoryStream()) using (var memStream = new MemoryStream())
{ {
Assert.Throws<ImageFormatException>(() => image.SaveAsJpeg(memStream)); Assert.Throws<ImageFormatException>(() => image.SaveAsJpeg(memStream));
} }
@ -274,6 +274,9 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void ExifTypeUndefined() public void ExifTypeUndefined()
{ {
// This image contains an 802 byte EXIF profile
// It has a tag with an index offset of 18,481,152 bytes (overrunning the data)
Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage();
Assert.NotNull(image); Assert.NotNull(image);

12
tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Collections.ObjectModel; using System.Collections.Generic;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using Xunit; using Xunit;
@ -12,10 +12,9 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void Read_DataIsEmpty_ReturnsEmptyCollection() public void Read_DataIsEmpty_ReturnsEmptyCollection()
{ {
ExifReader reader = new ExifReader(); var reader = new ExifReader(new byte[] { });
byte[] data = new byte[] { };
Collection<ExifValue> result = reader.Read(data); IList<ExifValue> result = reader.ReadValues();
Assert.Equal(0, result.Count); Assert.Equal(0, result.Count);
} }
@ -23,10 +22,9 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void Read_DataIsMinimal_ReturnsEmptyCollection() public void Read_DataIsMinimal_ReturnsEmptyCollection()
{ {
ExifReader reader = new ExifReader(); var reader = new ExifReader(new byte[] { 69, 120, 105, 102, 0, 0 });
byte[] data = new byte[] { 69, 120, 105, 102, 0, 0 };
Collection<ExifValue> result = reader.Read(data); IList<ExifValue> result = reader.ReadValues();
Assert.Equal(0, result.Count); Assert.Equal(0, result.Count);
} }

Loading…
Cancel
Save