Browse Source

Merge pull request #506 from carbon/master

Improve Code Quality
af/merge-core
James Jackson-South 8 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. 4
      src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
  26. 2
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  27. 2
      src/ImageSharp/Formats/Bmp/BmpConstants.cs
  28. 4
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  29. 16
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  30. 4
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  31. 17
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  32. 28
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  33. 4
      src/ImageSharp/Formats/Gif/PackedField.cs
  34. 19
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  35. 12
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  36. 7
      src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
  37. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
  38. 11
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  39. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs
  40. 11
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  41. 9
      src/ImageSharp/Formats/Png/PngChunk.cs
  42. 92
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  43. 22
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  44. 48
      src/ImageSharp/Formats/Png/PngHeader.cs
  45. 1
      src/ImageSharp/IImageFrameCollection.cs
  46. 8
      src/ImageSharp/Image.FromStream.cs
  47. 24
      src/ImageSharp/ImageExtensions.cs
  48. 16
      src/ImageSharp/Memory/SpanHelper.cs
  49. 25
      src/ImageSharp/MetaData/ImageProperty.cs
  50. 50
      src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs
  51. 60
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  52. 394
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  53. 281
      src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
  54. 191
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  55. 410
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  56. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
  57. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
  58. 2
      src/ImageSharp/PixelFormats/Alpha8.cs
  59. 2
      src/ImageSharp/PixelFormats/Bgr24.cs
  60. 2
      src/ImageSharp/PixelFormats/Bgra32.cs
  61. 2
      src/ImageSharp/PixelFormats/Bgra4444.cs
  62. 2
      src/ImageSharp/PixelFormats/Bgra5551.cs
  63. 6
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  64. 2
      src/ImageSharp/PixelFormats/HalfVector4.cs
  65. 2
      src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
  66. 2
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  67. 2
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  68. 2
      src/ImageSharp/PixelFormats/NormalizedShort4.cs
  69. 2
      src/ImageSharp/PixelFormats/Rgb24.cs
  70. 1
      src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
  71. 2
      src/ImageSharp/PixelFormats/RgbaVector.cs
  72. 2
      src/ImageSharp/PixelFormats/Short2.cs
  73. 2
      src/ImageSharp/PixelFormats/Short4.cs
  74. 2
      src/ImageSharp/Primitives/DenseMatrix{T}.cs
  75. 275
      src/ImageSharp/Primitives/LongRational.cs
  76. 33
      src/ImageSharp/Primitives/Rational.cs
  77. 33
      src/ImageSharp/Primitives/SignedRational.cs
  78. 16
      tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs
  79. 18
      tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs
  80. 19
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
  81. 12
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs

2
src/ImageSharp/ColorSpaces/CieLab.cs

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

7
src/ImageSharp/ColorSpaces/CieLch.cs

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

7
src/ImageSharp/ColorSpaces/CieLchuv.cs

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

7
src/ImageSharp/ColorSpaces/CieLuv.cs

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

7
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

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

7
src/ImageSharp/ColorSpaces/CieXyy.cs

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

7
src/ImageSharp/ColorSpaces/CieXyz.cs

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

7
src/ImageSharp/ColorSpaces/Cmyk.cs

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

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

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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
float l = input.L, a = input.A, b = input.B;
float fy = (l + 16) / 116F;

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

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
{
@ -44,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieLch input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch Convert(CieLab input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv Convert(CieLchuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLchuv Convert(CieLuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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
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.
/// One of the specifiers of <see cref="IRgbWorkingSpace"/>.
/// </summary>
internal struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
@ -76,12 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is RgbPrimariesChromaticityCoordinates)
{
return this.Equals((RgbPrimariesChromaticityCoordinates)obj);
}
return false;
return obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/>

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

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

7
src/ImageSharp/ColorSpaces/Hsl.cs

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

7
src/ImageSharp/ColorSpaces/Hsv.cs

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

7
src/ImageSharp/ColorSpaces/HunterLab.cs

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

7
src/ImageSharp/ColorSpaces/LinearRgb.cs

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

7
src/ImageSharp/ColorSpaces/Lms.cs

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

7
src/ImageSharp/ColorSpaces/Rgb.cs

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

7
src/ImageSharp/ColorSpaces/YCbCr.cs

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

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

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

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

@ -58,4 +58,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
PNG = 5
}
}
}

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

@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "bm", "bmp", "dip" };
}
}
}

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

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
}
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(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 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);
@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
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++)
{
@ -581,13 +581,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.currentStream.Read(data, 0, BmpFileHeader.Size);
this.fileHeader = new BmpFileHeader
{
Type = BitConverter.ToInt16(data, 0),
FileSize = BitConverter.ToInt32(data, 2),
Reserved = BitConverter.ToInt32(data, 6),
Offset = BitConverter.ToInt32(data, 10)
};
this.fileHeader = new BmpFileHeader(
type: BitConverter.ToInt16(data, 0),
fileSize: BitConverter.ToInt32(data, 2),
reserved: BitConverter.ToInt32(data, 6),
offset: BitConverter.ToInt32(data, 10));
}
/// <summary>

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

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -28,4 +26,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
encoder.Encode(image, stream);
}
}
}
}

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

@ -55,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// 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,
Height = image.Height,
@ -69,12 +69,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ClrImportant = 0
};
BmpFileHeader fileHeader = new BmpFileHeader
{
Type = 19778, // BM
Offset = 54,
FileSize = 54 + infoHeader.ImageSize
};
var fileHeader = new BmpFileHeader(
type: 19778, // BM
offset: 54,
reserved: 0,
fileSize: 54 + infoHeader.ImageSize);
WriteHeader(writer, fileHeader);
this.WriteInfo(writer, infoHeader);
@ -92,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data.
/// </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.FileSize);

28
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
/// (i.e. least-significant byte first).
/// </remarks>
internal sealed class BmpFileHeader
internal readonly struct BmpFileHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.
/// </summary>
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>
/// Gets or sets the Bitmap identifier.
/// Gets the Bitmap identifier.
/// The field used to identify the bitmap file: 0x42 0x4D
/// (Hex code points for B and M)
/// </summary>
public short Type { get; set; }
public short Type { get; }
/// <summary>
/// Gets or sets the size of the bitmap file in bytes.
/// Gets the size of the bitmap file in bytes.
/// </summary>
public int FileSize { get; set; }
public int FileSize { get; }
/// <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.
/// </summary>
public int Reserved { get; set; }
public int Reserved { get; }
/// <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.
/// </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/>
public override bool Equals(object obj)
{
PackedField? field = obj as PackedField?;
return this.Byte == field?.Byte;
return obj is PackedField other && this.Equals(other);
}
/// <inheritdoc/>

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

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

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

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

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

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

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

@ -427,10 +427,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
if (this.isExif)
{
ExifValue horizontal = this.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
ExifValue vertical = this.MetaData.ExifProfile.GetValue(ExifTag.YResolution);
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizonalTag)
? ((Rational)horizonalTag.Value).ToDouble()
: 0;
double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 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>
/// Represents a jpeg file marker
/// </summary>
internal struct PdfJsFileMarker
internal readonly struct PdfJsFileMarker
{
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsFileMarker"/> struct.
@ -34,9 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
/// <summary>
/// Gets or sets a value indicating whether the current marker is invalid
/// Gets a value indicating whether the current marker is invalid
/// </summary>
public bool Invalid { get; set; }
public bool Invalid { get; }
/// <summary>
/// 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)
{
ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution);
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double horizontalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
? ((Rational)horizontalTag.Value).ToDouble()
: 0;
double verticalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0)
{

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

@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal sealed class PngChunk
{
public PngChunk(int length)
{
this.Length = length;
}
/// <summary>
/// Gets or sets the length.
/// Gets the length.
/// An unsigned integer giving the number of bytes in the chunk's
/// data field. The length counts only the data field, not itself,
/// the chunk type code, or the CRC. Zero is a valid length
/// </summary>
public int Length { get; set; }
public int Length { get; }
/// <summary>
/// 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))
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{
try
{
switch (currentChunk.Type)
switch (chunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array);
this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array);
this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break;
case PngChunkTypes.Data:
if (image == null)
@ -237,23 +236,23 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image);
}
deframeStream.AllocateNewBytes(currentChunk.Length);
deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.currentStream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length);
byte[] pal = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length);
byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length);
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -263,10 +262,10 @@ namespace SixLabors.ImageSharp.Formats.Png
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
if (chunk.Data != null)
{
currentChunk.Data.Dispose();
currentChunk.Data = null;
chunk.Data.Dispose();
chunk.Data = null;
}
}
}
@ -297,25 +296,24 @@ namespace SixLabors.ImageSharp.Formats.Png
this.currentStream.Skip(8);
try
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{
try
{
switch (currentChunk.Type)
switch (chunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array);
this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array);
this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break;
case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk);
this.SkipChunkDataAndCrc(chunk);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length);
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -325,9 +323,9 @@ namespace SixLabors.ImageSharp.Formats.Png
finally
{
// 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();
}
if (this.header == null)
if (this.header.Width == 0 && this.header.Height == 0)
{
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>
private void ReadHeaderChunk(ReadOnlySpan<byte> data)
{
this.header = new PngHeader
{
Width = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)),
Height = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
BitDepth = data[8],
ColorType = (PngColorType)data[9],
CompressionMethod = data[10],
FilterMethod = data[11],
InterlaceMethod = (PngInterlaceMode)data[12]
};
this.header = new PngHeader(
width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)),
height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
bitDepth: data[8],
colorType: (PngColorType)data[9],
compressionMethod: data[10],
filterMethod: data[11],
interlaceMethod: (PngInterlaceMode)data[12]);
}
/// <summary>
@ -1208,36 +1204,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>
/// The <see cref="PngChunk"/>.
/// </returns>
private PngChunk ReadChunk()
private bool TryReadChunk(out PngChunk chunk)
{
var chunk = new PngChunk();
this.ReadChunkLength(chunk);
int length = this.ReadChunkLength();
if (chunk.Length == -1)
if (length == -1)
{
chunk = default;
// IEND
return null;
return false;
}
chunk = new PngChunk(length);
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.
// That lets us read one byte at a time until we reach a known chunk.
this.currentStream.Position -= 3;
return chunk;
return true;
}
this.ReadChunkType(chunk);
if (chunk.Type == PngChunkTypes.Data)
{
return chunk;
return true;
}
this.ReadChunkData(chunk);
this.ReadChunkCrc(chunk);
return chunk;
return true;
}
/// <summary>
@ -1314,21 +1314,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Calculates the length of the given chunk.
/// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid.
/// </exception>
private void ReadChunkLength(PngChunk chunk)
private int ReadChunkLength()
{
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
if (numBytes < 4)
{
chunk.Length = -1;
return;
return -1;
}
chunk.Length = BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
}
/// <summary>

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

@ -212,16 +212,14 @@ namespace SixLabors.ImageSharp.Formats.Png
this.bytesPerPixel = this.CalculateBytesPerPixel();
var header = new PngHeader
{
Width = image.Width,
Height = image.Height,
ColorType = this.pngColorType,
BitDepth = this.bitDepth,
FilterMethod = 0, // None
CompressionMethod = 0,
InterlaceMethod = 0
};
var header = new PngHeader(
width: image.Width,
height: image.Height,
colorType: this.pngColorType,
bitDepth: this.bitDepth,
filterMethod: 0, // None
compressionMethod: 0,
interlaceMethod: 0);
this.WriteHeaderChunk(stream, header);
@ -415,7 +413,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</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, 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="header">The <see cref="PngHeader"/>.</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>
{
// 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>
/// Represents the png header chunk.
/// </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>
/// 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>
public int Width { get; set; }
public int Width { get; }
/// <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>
public int Height { get; set; }
public int Height { get; }
/// <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
/// 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.
/// </summary>
public byte BitDepth { get; set; }
public byte BitDepth { get; }
/// <summary>
/// Gets or sets the color type.
/// Gets the color type.
/// Color type is a integer that describes the interpretation of the
/// image data. Color type codes represent sums of the following values:
/// 1 (palette used), 2 (color used), and 4 (alpha channel used).
/// </summary>
public PngColorType ColorType { get; set; }
public PngColorType ColorType { get; }
/// <summary>
/// Gets or sets the compression method.
/// Gets the compression method.
/// Indicates the method used to compress the image data. At present,
/// only compression method 0 (deflate/inflate compression with a sliding
/// window of at most 32768 bytes) is defined.
/// </summary>
public byte CompressionMethod { get; set; }
public byte CompressionMethod { get; }
/// <summary>
/// Gets or sets the preprocessing method.
/// Gets the preprocessing method.
/// Indicates the preprocessing method applied to the image
/// data before compression. At present, only filter method 0
/// (adaptive filtering with five basic filter types) is defined.
/// </summary>
public byte FilterMethod { get; set; }
public byte FilterMethod { get; }
/// <summary>
/// Gets or sets the transmission order.
/// Gets the transmission order.
/// Indicates the transmission order of the image data.
/// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace).
/// </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.
using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;

8
src/ImageSharp/Image.FromStream.cs

@ -183,15 +183,15 @@ namespace SixLabors.ImageSharp
return data.img;
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
var sb = new StringBuilder();
sb.AppendLine("Image cannot be loaded. Available decoders:");
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)

24
src/ImageSharp/ImageExtensions.cs

@ -35,28 +35,28 @@ namespace SixLabors.ImageSharp
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
var sb = new StringBuilder();
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)
{
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);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
var sb = new StringBuilder();
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)
{
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);
@ -97,15 +97,15 @@ namespace SixLabors.ImageSharp
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
var sb = new StringBuilder();
sb.AppendLine("Can't find encoder for provided mime type. Available encoded:");
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);

16
src/ImageSharp/Memory/SpanHelper.cs

@ -11,20 +11,6 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
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>
/// Copy all elements of 'source' into 'destination'.
/// </summary>
@ -35,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory
public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct
{
Copy(source, destination, Math.Min(source.Length, destination.Length));
source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination);
}
/// <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
/// or some other information.
/// </summary>
public class ImageProperty : IEquatable<ImageProperty>
public readonly struct ImageProperty : IEquatable<ImageProperty>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class.
/// Initializes a new instance of the <see cref="ImageProperty"/> struct.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="value">The value of the property.</param>
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.MetaData
}
/// <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.
/// </summary>
/// <param name="other">
@ -71,11 +71,6 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns>
public static bool operator ==(ImageProperty left, ImageProperty right)
{
if (ReferenceEquals(left, right))
{
return true;
}
return left.Equals(right);
}
@ -110,9 +105,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns>
public override bool Equals(object obj)
{
ImageProperty other = obj as ImageProperty;
return this.Equals(other);
return obj is ImageProperty other && this.Equals(other);
}
/// <summary>
@ -155,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData
/// <param name="other">An object to compare with this object.</param>
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);
}
}

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

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

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

@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// The collection of EXIF values
/// </summary>
private Collection<ExifValue> values;
private List<ExifValue> values;
/// <summary>
/// The list of invalid EXIF tags
/// </summary>
private List<ExifTag> invalidTags;
private IReadOnlyList<ExifTag> invalidTags;
/// <summary>
/// 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);
if (other.values != null)
{
this.values = new Collection<ExifValue>();
foreach (ExifValue value in other.values)
this.values = new List<ExifValue>(other.Values.Count);
foreach (ExifValue value in other.Values)
{
this.values.Add(new ExifValue(value));
}
@ -92,21 +93,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// Gets or sets which parts will be written when the profile is added to an image.
/// </summary>
public ExifParts Parts
{
get;
set;
}
public ExifParts Parts { get; set; }
/// <summary>
/// Gets the tags that where found but contained an invalid value.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the values of this EXIF profile.
/// </summary>
public IEnumerable<ExifValue> Values
public IReadOnlyList<ExifValue> Values
{
get
{
@ -163,6 +160,31 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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>
/// Removes the value with the specified tag.
/// </summary>
@ -193,16 +215,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param>
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;
}
}
var newExifValue = ExifValue.Create(tag, value);
this.values.Add(newExifValue);
}
@ -262,12 +286,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.data == null)
{
this.values = new Collection<ExifValue>();
this.values = new List<ExifValue>();
return;
}
var reader = new ExifReader();
this.values = reader.Read(this.data);
var reader = new ExifReader(this.data);
this.values = reader.ReadValues();
this.invalidTags = new List<ExifTag>(reader.InvalidTags);
this.thumbnailOffset = (int)reader.ThumbnailOffset;
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.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
@ -15,38 +18,37 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
internal sealed class ExifReader
{
private readonly Collection<ExifTag> invalidTags = new Collection<ExifTag>();
private byte[] exifData;
private uint currentIndex;
private bool isLittleEndian;
private readonly List<ExifTag> invalidTags = new List<ExifTag>();
private readonly byte[] exifData;
private int position;
private Endianness endianness = Endianness.BigEndian;
private uint exifOffset;
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>
/// Gets the invalid tags.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the thumbnail length in the byte stream
/// </summary>
public uint ThumbnailLength
{
get;
private set;
}
public uint ThumbnailLength { get; private set; }
/// <summary>
/// Gets the thumbnail offset position in the byte stream
/// </summary>
public uint ThumbnailOffset
{
get;
private set;
}
public uint ThumbnailOffset { get; private set; }
/// <summary>
/// Gets the remaining length.
@ -55,81 +57,78 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.currentIndex >= this.exifData.Length)
if (this.position >= this.exifData.Length)
{
return 0;
}
return this.exifData.Length - (int)this.currentIndex;
return this.exifData.Length - this.position;
}
}
/// <summary>
/// Reads and returns the collection of EXIF values.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>
/// The <see cref="Collection{ExifValue}"/>.
/// </returns>
public Collection<ExifValue> Read(byte[] data)
public List<ExifValue> ReadValues()
{
DebugGuard.NotNull(data, nameof(data));
var result = new Collection<ExifValue>();
var values = new List<ExifValue>();
this.exifData = data;
if (this.GetString(4) == "Exif")
if (this.ReadString(4) == "Exif")
{
if (this.GetShort() != 0)
if (this.ReadUInt16() != 0)
{
return result;
return values;
}
this.startIndex = 6;
}
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();
this.AddValues(result, ifdOffset);
uint ifdOffset = this.ReadUInt32();
this.AddValues(values, (int)ifdOffset);
uint thumbnailOffset = this.GetLong();
this.GetThumbnail(thumbnailOffset);
uint thumbnailOffset = this.ReadUInt32();
this.GetThumbnail((int)thumbnailOffset);
if (this.exifOffset != 0)
{
this.AddValues(result, this.exifOffset);
this.AddValues(values, (int)this.exifOffset);
}
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 length = data.Length / dataTypeSize;
TDataType[] result = new TDataType[length];
byte[] buffer = new byte[dataTypeSize];
var result = new TDataType[length];
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);
}
@ -137,14 +136,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result;
}
private static byte ToByte(byte[] data)
{
return data[0];
}
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[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');
if (nullCharIndex != -1)
{
@ -159,15 +167,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
/// <param name="values">The values.</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;
ushort count = this.GetShort();
this.position = this.startIndex + index;
int count = this.ReadUInt16();
for (ushort i = 0; i < count; i++)
for (int i = 0; i < count; i++)
{
ExifValue value = this.CreateValue();
if (value == null)
if (!this.TryReadValue(out ExifValue value))
{
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;
}
@ -220,106 +227,116 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifDataType.Unknown:
return null;
case ExifDataType.Ascii:
return ToString(data);
return this.ConvertToString(buffer);
case ExifDataType.Byte:
if (numberOfComponents == 1)
{
return ToByte(data);
return this.ConvertToByte(buffer);
}
return data;
return buffer.ToArray();
case ExifDataType.DoubleFloat:
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:
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:
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:
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:
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:
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:
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:
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:
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:
if (numberOfComponents == 1)
{
return ToByte(data);
return this.ConvertToByte(buffer);
}
return data;
return buffer.ToArray();
default:
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)
{
return null;
exifValue = default;
return false;
}
ExifTag tag = this.ToEnum(this.GetShort(), ExifTag.Unknown);
ExifDataType dataType = this.ToEnum(this.GetShort(), ExifDataType.Unknown);
object value;
ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown);
uint type = this.ReadUInt16();
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.
// 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);
byte[] data = this.GetBytes(4);
this.TryReadSpan(4, out ReadOnlySpan<byte> offsetBuffer);
if (size > 4)
{
uint oldIndex = this.currentIndex;
this.currentIndex = this.ToLong(data) + this.startIndex;
int oldIndex = this.position;
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)
{
this.invalidTags.Add(tag);
this.currentIndex = oldIndex;
return null;
this.position = oldIndex;
exifValue = default;
return false;
}
value = this.ConvertValue(dataType, this.GetBytes(size), numberOfComponents);
this.currentIndex = oldIndex;
this.TryReadSpan((int)size, out ReadOnlySpan<byte> dataBuffer);
value = this.ConvertValue(dataType, dataBuffer, numberOfComponents);
this.position = oldIndex;
}
else
{
value = this.ConvertValue(dataType, data, numberOfComponents);
value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
}
bool isArray = value != null && numberOfComponents > 1;
return new ExifValue(tag, dataType, value, isArray);
exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents > 1);
return true;
}
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
@ -366,51 +404,57 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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];
Array.Copy(this.exifData, (int)this.currentIndex, data, 0, (int)length);
this.currentIndex += length;
span = new ReadOnlySpan<byte>(this.exifData, this.position, 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 (data == null || data.Length == 0)
if (this.TryReadSpan(length, out ReadOnlySpan<byte> span) && span.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);
foreach (ExifValue value in values)
{
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)
{
@ -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 denominator = BitConverter.ToUInt32(data, 4);
uint numerator = this.ConvertToUInt32(buffer.Slice(0, 4));
uint denominator = this.ConvertToUInt32(buffer.Slice(4, 4));
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 denominator = BitConverter.ToInt32(data, 4);
int numerator = this.ConvertToInt32(buffer.Slice(0, 4));
int denominator = this.ConvertToInt32(buffer.Slice(4, 4));
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);
}
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;
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
}
}

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>
public sealed class ExifValue : IEquatable<ExifValue>
{
/// <summary>
/// The exif value.
/// </summary>
private object exifValue;
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class
/// by making a copy from another exif value.
@ -34,30 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (!other.IsArray)
{
this.exifValue = other.exifValue;
this.Value = other.Value;
}
else
{
var array = (Array)other.exifValue;
this.exifValue = 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;
var array = (Array)other.Value;
this.Value = array.Clone();
}
}
@ -69,51 +46,32 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
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>
/// Gets the data type of the exif value.
/// </summary>
public ExifDataType DataType
{
get;
}
public ExifDataType DataType { get; }
/// <summary>
/// Gets a value indicating whether the value is an array.
/// </summary>
public bool IsArray
{
get;
}
public bool IsArray { get; }
/// <summary>
/// Gets the tag of the exif value.
/// </summary>
public ExifTag Tag
{
get;
}
public ExifTag Tag { get; }
/// <summary>
/// Gets or sets the value.
/// Gets the value.
/// </summary>
public object Value
{
get
{
return this.exifValue;
}
set
{
this.CheckValue(value);
this.exifValue = value;
}
}
public object Value { get; }
/// <summary>
/// Gets a value indicating whether the EXIF value has a value.
@ -122,14 +80,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.exifValue == null)
if (this.Value == null)
{
return false;
}
if (this.DataType == ExifDataType.Ascii)
{
return ((string)this.exifValue).Length > 0;
return ((string)this.Value).Length > 0;
}
return true;
@ -143,7 +101,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.exifValue == null)
if (this.Value == null)
{
return 4;
}
@ -163,12 +121,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
if (this.DataType == ExifDataType.Ascii)
{
return Encoding.UTF8.GetBytes((string)this.exifValue).Length;
return Encoding.UTF8.GetBytes((string)this.Value).Length;
}
if (this.IsArray)
{
return ((Array)this.exifValue).Length;
return ((Array)this.Value).Length;
}
return 1;
@ -217,12 +175,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
return this.Equals(obj as ExifValue);
return obj is ExifValue other && this.Equals(other);
}
/// <inheritdoc />
@ -241,7 +194,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return
this.Tag == other.Tag &&
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/>
@ -253,26 +218,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc/>
public override string ToString()
{
if (this.exifValue == null)
if (this.Value == null)
{
return null;
}
if (this.DataType == ExifDataType.Ascii)
{
return (string)this.exifValue;
return (string)this.Value;
}
if (!this.IsArray)
{
return this.ToString(this.exifValue);
return this.ToString(this.Value);
}
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(" ");
sb.Append(' ');
}
return sb.ToString();
@ -293,13 +258,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
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)
{
case ExifTag.ImageDescription:
@ -355,8 +313,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSDestBearingRef:
case ExifTag.GPSDestDistanceRef:
case ExifTag.GPSDateStamp:
exifValue = new ExifValue(tag, ExifDataType.Ascii, true);
break;
return new ExifValue(tag, ExifDataType.Ascii, value, true);
case ExifTag.ClipPath:
case ExifTag.VersionYear:
@ -369,13 +326,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.XPKeywords:
case ExifTag.XPSubject:
case ExifTag.GPSVersionID:
exifValue = new ExifValue(tag, ExifDataType.Byte, true);
break;
return new ExifValue(tag, ExifDataType.Byte, value, true);
case ExifTag.FaxProfile:
case ExifTag.ModeNumber:
case ExifTag.GPSAltitudeRef:
exifValue = new ExifValue(tag, ExifDataType.Byte, false);
break;
return new ExifValue(tag, ExifDataType.Byte, value, false);
case ExifTag.FreeOffsets:
case ExifTag.FreeByteCounts:
@ -389,8 +345,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.StripRowCounts:
case ExifTag.IntergraphRegisters:
case ExifTag.TimeZoneOffset:
exifValue = new ExifValue(tag, ExifDataType.Long, true);
break;
return new ExifValue(tag, ExifDataType.Long, value, true);
case ExifTag.SubfileType:
case ExifTag.SubIFDOffset:
case ExifTag.GPSIFDOffset:
@ -412,8 +367,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.FaxRecvParams:
case ExifTag.FaxRecvTime:
case ExifTag.ImageNumber:
exifValue = new ExifValue(tag, ExifDataType.Long, false);
break;
return new ExifValue(tag, ExifDataType.Long, value, false);
case ExifTag.WhitePoint:
case ExifTag.PrimaryChromaticities:
@ -428,8 +382,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSTimestamp:
case ExifTag.GPSDestLatitude:
case ExifTag.GPSDestLongitude:
exifValue = new ExifValue(tag, ExifDataType.Rational, true);
break;
return new ExifValue(tag, ExifDataType.Rational, value, true);
case ExifTag.XPosition:
case ExifTag.YPosition:
case ExifTag.XResolution:
@ -463,8 +417,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSImgDirection:
case ExifTag.GPSDestBearing:
case ExifTag.GPSDestDistance:
exifValue = new ExifValue(tag, ExifDataType.Rational, false);
break;
return new ExifValue(tag, ExifDataType.Rational, value, false);
case ExifTag.BitsPerSample:
case ExifTag.MinSampleValue:
@ -487,8 +440,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ISOSpeedRatings:
case ExifTag.SubjectArea:
case ExifTag.SubjectLocation:
exifValue = new ExifValue(tag, ExifDataType.Short, true);
break;
return new ExifValue(tag, ExifDataType.Short, value, true);
case ExifTag.OldSubfileType:
case ExifTag.Compression:
case ExifTag.PhotometricInterpretation:
@ -535,20 +488,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.Sharpness:
case ExifTag.SubjectDistanceRange:
case ExifTag.GPSDifferential:
exifValue = new ExifValue(tag, ExifDataType.Short, false);
break;
return new ExifValue(tag, ExifDataType.Short, value, false);
case ExifTag.Decode:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, true);
break;
return new ExifValue(tag, ExifDataType.SignedRational, value, true);
case ExifTag.ShutterSpeedValue:
case ExifTag.BrightnessValue:
case ExifTag.ExposureBiasValue:
case ExifTag.AmbientTemperature:
case ExifTag.WaterDepth:
case ExifTag.CameraElevationAngle:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, false);
break;
return new ExifValue(tag, ExifDataType.SignedRational, value, false);
case ExifTag.JPEGTables:
case ExifTag.OECF:
@ -565,18 +516,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ImageSourceData:
case ExifTag.GPSProcessingMethod:
case ExifTag.GPSAreaInformation:
exifValue = new ExifValue(tag, ExifDataType.Undefined, true);
break;
return new ExifValue(tag, ExifDataType.Undefined, value, true);
case ExifTag.FileSource:
case ExifTag.SceneType:
exifValue = new ExifValue(tag, ExifDataType.Undefined, false);
break;
return new ExifValue(tag, ExifDataType.Undefined, value, false);
case ExifTag.StripOffsets:
case ExifTag.TileByteCounts:
case ExifTag.ImageLayer:
exifValue = CreateNumber(tag, type, true);
break;
return CreateNumber(tag, value, true);
case ExifTag.ImageWidth:
case ExifTag.ImageLength:
case ExifTag.TileWidth:
@ -585,15 +535,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ConsecutiveBadFaxLines:
case ExifTag.PixelXDimension:
case ExifTag.PixelYDimension:
exifValue = CreateNumber(tag, type, false);
break;
return CreateNumber(tag, value, false);
default:
throw new NotSupportedException();
}
exifValue.Value = value;
return exifValue;
}
/// <summary>
@ -635,29 +581,35 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// Returns an EXIF value with a numeric type for the given tag.
/// </summary>
/// <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>
/// <returns>
/// The <see cref="ExifValue"/>.
/// </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))
{
return new ExifValue(tag, ExifDataType.Short, isArray);
return new ExifValue(tag, ExifDataType.Short, value, isArray);
}
if (type == typeof(short))
{
return new ExifValue(tag, ExifDataType.SignedShort, isArray);
return new ExifValue(tag, ExifDataType.SignedShort, value, isArray);
}
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>
@ -739,8 +691,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <returns>The <see cref="string"/></returns>
private string ToString(object value)
{
string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, value);
if (description != null)
if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description)
{
return description;
}
@ -788,7 +739,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private int GetHashCode(ExifValue exif)
{
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.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using SixLabors.ImageSharp.Primitives;
@ -19,299 +19,28 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
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>
/// Which parts will be written.
/// </summary>
private ExifParts allowedParts;
private Collection<ExifValue> values;
private Collection<int> dataOffsets;
private Collection<int> ifdIndexes;
private Collection<int> exifIndexes;
private Collection<int> gpsIndexes;
private IList<ExifValue> values;
private List<int> dataOffsets;
private List<int> ifdIndexes;
private List<int> exifIndexes;
private List<int> gpsIndexes;
/// <summary>
/// Initializes a new instance of the <see cref="ExifWriter"/> class.
/// </summary>
/// <param name="values">The values.</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.allowedParts = allowedParts;
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, IfdTags);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, GPSTags);
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps);
}
/// <summary>
@ -360,6 +89,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
length += 10 + 4 + 2;
byte[] result = new byte[length];
result[0] = (byte)'E';
result[1] = (byte)'x';
result[2] = (byte)'i';
@ -377,17 +107,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (exifLength > 0)
{
this.values[exifIndex].Value = ifdOffset + ifdLength;
this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength);
}
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 = Write(BitConverter.GetBytes(thumbnailOffset), result, i);
i = WriteUInt32(thumbnailOffset, result, i);
i = this.WriteData(this.ifdIndexes, result, i);
if (exifLength > 0)
@ -402,19 +132,61 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
i = this.WriteData(this.gpsIndexes, result, i);
}
Write(BitConverter.GetBytes((ushort)0), result, i);
WriteUInt16((ushort)0, result, i);
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;
}
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)
{
@ -430,14 +202,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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)
{
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++)
{
ExifValue value = this.values[i];
@ -457,7 +229,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result;
}
private uint GetLength(IEnumerable<int> indexes)
private uint GetLength(IList<int> indexes)
{
uint length = 0;
@ -494,7 +266,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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)
{
@ -509,7 +281,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
ExifValue value = this.values[index];
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);
}
}
@ -517,11 +289,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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)
{
@ -531,9 +303,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
foreach (int index in indexes)
{
ExifValue value = this.values[index];
newOffset = Write(BitConverter.GetBytes((ushort)value.Tag), destination, newOffset);
newOffset = Write(BitConverter.GetBytes((ushort)value.DataType), destination, newOffset);
newOffset = Write(BitConverter.GetBytes((uint)value.NumberOfComponents), destination, newOffset);
newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset);
newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset);
newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset);
if (value.Length > 4)
{
@ -550,23 +322,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8;
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
}
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);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8;
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
}
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)
{
@ -577,24 +345,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
destination[offset] = (byte)value;
return offset + 1;
case ExifDataType.DoubleFloat:
return Write(BitConverter.GetBytes((double)value), destination, offset);
return WriteDouble((double)value, destination, offset);
case ExifDataType.Short:
return Write(BitConverter.GetBytes((ushort)value), destination, offset);
return WriteUInt16((ushort)value, destination, offset);
case ExifDataType.Long:
return Write(BitConverter.GetBytes((uint)value), destination, offset);
return WriteUInt32((uint)value, destination, offset);
case ExifDataType.Rational:
return this.WriteRational((Rational)value, destination, offset);
WriteRational(destination.Slice(offset, 8), (Rational)value);
return offset + 8;
case ExifDataType.SignedByte:
destination[offset] = unchecked((byte)((sbyte)value));
return offset + 1;
case ExifDataType.SignedLong:
return Write(BitConverter.GetBytes((int)value), destination, offset);
return WriteInt32((int)value, destination, offset);
case ExifDataType.SignedShort:
return Write(BitConverter.GetBytes((short)value), destination, offset);
return WriteInt16((short)value, destination, offset);
case ExifDataType.SignedRational:
return this.WriteSignedRational((SignedRational)value, destination, offset);
WriteSignedRational(destination.Slice(offset, 8), (SignedRational)value);
return offset + 8;
case ExifDataType.SingleFloat:
return Write(BitConverter.GetBytes((float)value), destination, offset);
return WriteSingle((float)value, destination, offset);
default:
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 obj is IccParametricCurve && this.Equals((IccParametricCurve)obj);
return obj is IccParametricCurve other && this.Equals(other);
}
/// <inheritdoc/>

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

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

2
src/ImageSharp/PixelFormats/Bgr24.cs

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

2
src/ImageSharp/PixelFormats/Bgra32.cs

@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <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/>
public override int GetHashCode()

2
src/ImageSharp/PixelFormats/Bgra4444.cs

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

2
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra5551) && this.Equals((Bgra5551)obj);
return obj is Bgra5551 other && this.Equals(other);
}
/// <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));
}
TPixel result = default(TPixel);
Rgba32 rgba = new Rgba32(
TPixel result = default;
var rgba = new Rgba32(
(byte)(packedValue >> 24),
(byte)(packedValue >> 16),
(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>
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));
return color;
}

2
src/ImageSharp/PixelFormats/HalfVector4.cs

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

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

@ -735,7 +735,7 @@ namespace SixLabors.ImageSharp.PixelFormats
private static TPixel[] GetWebSafePalette()
{
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>();
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length);

2
src/ImageSharp/PixelFormats/NormalizedByte2.cs

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

2
src/ImageSharp/PixelFormats/NormalizedByte4.cs

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

2
src/ImageSharp/PixelFormats/NormalizedShort4.cs

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

2
src/ImageSharp/PixelFormats/Rgb24.cs

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

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

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

2
src/ImageSharp/PixelFormats/RgbaVector.cs

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

2
src/ImageSharp/PixelFormats/Short2.cs

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

2
src/ImageSharp/PixelFormats/Short4.cs

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

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

@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Primitives
}
/// <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/>
public override int GetHashCode() => this.Data.GetHashCode();

275
src/ImageSharp/Primitives/LongRational.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Primitives
/// <remarks>
/// This is a very simplified implementation of a rational number designed for use with metadata only.
/// </remarks>
internal struct LongRational : IEquatable<LongRational>
internal readonly struct LongRational : IEquatable<LongRational>
{
/// <summary>
/// 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.
/// </param>
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.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>
/// Gets the numerator of a number.
/// </summary>
public long Numerator
{
get;
private set;
}
public long Numerator { get; }
/// <summary>
/// Gets the denominator of a number.
/// </summary>
public long Denominator
{
get;
private set;
}
public long Denominator { get; }
/// <summary>
/// Gets a value indicating whether this instance is indeterminate.
/// </summary>
public bool IsIndeterminate
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == 0;
}
}
public bool IsIndeterminate => this.Denominator == 0 && this.Numerator == 0;
/// <summary>
/// Gets a value indicating whether this instance is an integer (n, 1)
@ -155,76 +54,28 @@ namespace SixLabors.ImageSharp.Primitives
/// <summary>
/// Gets a value indicating whether this instance is equal to negative infinity (-1, 0)
/// </summary>
public bool IsNegativeInfinity
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == -1;
}
}
public bool IsNegativeInfinity => this.Denominator == 0 && this.Numerator == -1;
/// <summary>
/// Gets a value indicating whether this instance is equal to positive infinity (1, 0)
/// </summary>
public bool IsPositiveInfinity
{
get
{
if (this.Denominator != 0)
{
return false;
}
return this.Numerator == 1;
}
}
public bool IsPositiveInfinity => this.Denominator == 0 && this.Numerator == 1;
/// <summary>
/// Gets a value indicating whether this instance is equal to 0 (0, 1)
/// </summary>
public bool IsZero
{
get
{
if (this.Denominator != 1)
{
return false;
}
return this.Numerator == 0;
}
}
public bool IsZero => this.Denominator == 1 && this.Numerator == 0;
/// <inheritdoc/>
public bool Equals(LongRational other)
{
if (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);
return this.Numerator == other.Numerator && this.Denominator == other.Denominator;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
return ((this.Numerator * 397) ^ this.Denominator).GetHashCode();
}
/// <inheritdoc/>
@ -268,86 +119,108 @@ namespace SixLabors.ImageSharp.Primitives
return this.Numerator.ToString(provider);
}
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
sb.Append(this.Numerator.ToString(provider));
sb.Append("/");
sb.Append('/');
sb.Append(this.Denominator.ToString(provider));
return sb.ToString();
}
/// <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>
/// <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)
/// <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 static LongRational FromDouble(double value, bool bestPrecision)
{
return right == 0 ? left : GreatestCommonDivisor(right, left % right);
}
if (double.IsNaN(value))
{
return new LongRational(0, 0);
}
/// <summary>
/// Simplifies the <see cref="LongRational"/>
/// </summary>
private void Simplify()
{
if (this.IsIndeterminate)
if (double.IsPositiveInfinity(value))
{
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)
{
this.Denominator = 0;
return;
return new LongRational(0, 0);
}
if (this.Numerator == this.Denominator)
{
this.Numerator = 1;
this.Denominator = 1;
return new LongRational(1, 1);
}
long gcd = GreatestCommonDivisor(Math.Abs(this.Numerator), Math.Abs(this.Denominator));
if (gcd > 1)
{
this.Numerator = this.Numerator / gcd;
this.Denominator = this.Denominator / gcd;
return new LongRational(this.Numerator / gcd, this.Denominator / gcd);
}
}
/// <summary>
/// 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();
return this;
}
}
}

33
src/ImageSharp/Primitives/Rational.cs

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

33
src/ImageSharp/Primitives/SignedRational.cs

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

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

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

@ -18,13 +18,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void AreEqual()
{
ImageProperty property1 = new ImageProperty("Foo", "Bar");
ImageProperty property2 = new ImageProperty("Foo", "Bar");
ImageProperty property3 = null;
var property1 = new ImageProperty("Foo", "Bar");
var property2 = new ImageProperty("Foo", "Bar");
Assert.Equal(property1, property2);
Assert.True(property1 == property2);
Assert.Null(property3);
}
/// <summary>
@ -33,15 +31,13 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void AreNotEqual()
{
ImageProperty property1 = new ImageProperty("Foo", "Bar");
ImageProperty property2 = new ImageProperty("Foo", "Foo");
ImageProperty property3 = new ImageProperty("Bar", "Bar");
ImageProperty property4 = new ImageProperty("Foo", null);
var property1 = new ImageProperty("Foo", "Bar");
var property2 = new ImageProperty("Foo", "Foo");
var property3 = new ImageProperty("Bar", "Bar");
var property4 = new ImageProperty("Foo", null);
Assert.False(property1.Equals("Foo"));
Assert.NotNull(property1);
Assert.NotEqual(property1, property2);
Assert.True(property1 != property2);
@ -66,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void ConstructorAssignsProperties()
{
ImageProperty property = new ImageProperty("Foo", null);
var property = new ImageProperty("Foo", null);
Assert.Equal("Foo", property.Name);
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);
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));
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests
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));
@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests
value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
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);
@ -209,11 +209,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Syncs()
{
ExifProfile exifProfile = new ExifProfile();
var exifProfile = new ExifProfile();
exifProfile.SetValue(ExifTag.XResolution, new Rational(200));
exifProfile.SetValue(ExifTag.YResolution, new Rational(300));
ImageMetaData metaData = new ImageMetaData();
var metaData = new ImageMetaData();
metaData.ExifProfile = exifProfile;
metaData.HorizontalResolution = 200;
metaData.VerticalResolution = 300;
@ -255,17 +255,17 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void WriteTooLargeProfile()
{
StringBuilder junk = new StringBuilder();
var junk = new StringBuilder();
for (int i = 0; i < 65500; 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.SetValue(ExifTag.ImageDescription, junk.ToString());
using (MemoryStream memStream = new MemoryStream())
using (var memStream = new MemoryStream())
{
Assert.Throws<ImageFormatException>(() => image.SaveAsJpeg(memStream));
}
@ -274,6 +274,9 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
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();
Assert.NotNull(image);

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

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

Loading…
Cancel
Save