diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs index cb08d08bf9..ce5c6c1186 100644 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ b/src/ImageSharp/ColorSpaces/CieLab.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents a that has L, A, B values set to zero. /// - public static readonly CieLab Empty = default(CieLab); + public static readonly CieLab Empty = default; /// /// The backing vector for SIMD support. diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 94443fd863..1b9cf9c2b7 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs index 705b770d35..7ec27806d8 100644 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs index b0ae048ab7..e46b736a75 100644 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index d0a70dd191..d54de43bbb 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs index 751830a0ba..9633f83ad0 100644 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index 0f1866009b..eedfed0798 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index 2eb148a8c3..2e44ea920a 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs index 0a5ae3627e..53d9c927ad 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs index 22308260c2..454601b884 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs index 35fae30e83..0b1ebae0ed 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs index aa4614f9ca..ec73a830f6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs index fc6554a905..eb523806a4 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs index f0d7a80a22..7a9dd2c6a7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs index 50e8335ed6..7a264fdfe2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs +++ b/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; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs index d279aba850..8afe2ffa05 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/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 . /// - internal struct RgbPrimariesChromaticityCoordinates : IEquatable + internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable { /// /// Initializes a new instance of the struct. @@ -76,12 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// 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); } /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 5a5c39647f..530c016916 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -73,12 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// 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); } /// diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs index 1944ac0c6b..3b2ceae27f 100644 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs index fdfbfe5dd1..f646eb29d0 100644 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index de42518d76..4ace27def9 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index b8c446285a..f2dc297a01 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs index 72ac16f213..09c20269ab 100644 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index ac86cfbf06..1282394670 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/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); } /// diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs index 44a0b245d5..a6e27de94b 100644 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ b/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); } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index d08487cf27..0029a6b68d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// 32 bits per pixel. Each pixel consists of 4 bytes. /// - Pixel32 = 4, + Pixel32 = 4 } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index 1280498acb..22b12346fb 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -58,4 +58,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// PNG = 5 } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs index b7291bb99e..99799b619c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs @@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public static readonly IEnumerable FileExtensions = new[] { "bm", "bmp", "dip" }; } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 78a9de6c45..d3cb50d6ba 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { - Guard.NotNull(stream, "stream"); + Guard.NotNull(stream, nameof(stream)); return new BmpDecoderCore(configuration, this).Decode(stream); } @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public IImageInfo Identify(Configuration configuration, Stream stream) { - Guard.NotNull(stream, "stream"); + Guard.NotNull(stream, nameof(stream)); return new BmpDecoderCore(configuration, this).Identify(stream); } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 9f4dba5b4f..dfbd44c046 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/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(width, height)) + using (Buffer2D buffer = this.memoryManager.AllocateClean2D(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)); } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index d80e43c63b..9edd0fcd4e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/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); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 66c8b6c086..be7c1d2e55 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/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 /// /// The containing the header data. /// - 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); diff --git a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs index 4255ecae49..ed17164a22 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs +++ b/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). /// - internal sealed class BmpFileHeader + internal readonly struct BmpFileHeader { /// /// Defines of the data structure in the bitmap file. /// 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; + } + /// - /// 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) /// - public short Type { get; set; } + public short Type { get; } /// - /// Gets or sets the size of the bitmap file in bytes. + /// Gets the size of the bitmap file in bytes. /// - public int FileSize { get; set; } + public int FileSize { get; } /// - /// 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. /// - public int Reserved { get; set; } + public int Reserved { get; } /// - /// 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. /// - public int Offset { get; set; } + public int Offset { get; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/PackedField.cs b/src/ImageSharp/Formats/Gif/PackedField.cs index 28a415e2b8..969449a9f9 100644 --- a/src/ImageSharp/Formats/Gif/PackedField.cs +++ b/src/ImageSharp/Formats/Gif/PackedField.cs @@ -169,9 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public override bool Equals(object obj) { - PackedField? field = obj as PackedField?; - - return this.Byte == field?.Byte; + return obj is PackedField other && this.Equals(other); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs index 11a456ef9b..8a571fa6b7 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -241,19 +241,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// 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(); } /// @@ -273,12 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// 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); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs index f45b5df4eb..3f71c498b2 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs @@ -496,19 +496,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// 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)] diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs index d55e36bd48..40059c5a0f 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs @@ -94,12 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// 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); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs index c856fd04a6..afe4794a23 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs +++ b/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 /// 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); } /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 0125d2703b..a4fbb17be3 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/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) { diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs index d6ff1e9eda..8e51c0b7cc 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Represents a jpeg file marker /// - internal struct PdfJsFileMarker + internal readonly struct PdfJsFileMarker { /// /// Initializes a new instance of the struct. @@ -34,9 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } /// - /// Gets or sets a value indicating whether the current marker is invalid + /// Gets a value indicating whether the current marker is invalid /// - public bool Invalid { get; set; } + public bool Invalid { get; } /// /// Gets the position of the marker within a stream diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index a600658b02..30b8158e73 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/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) { diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 2483a3ad9d..b944b43a34 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png /// internal sealed class PngChunk { + public PngChunk(int length) + { + this.Length = length; + } + /// - /// 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 /// - public int Length { get; set; } + public int Length { get; } /// /// Gets or sets the chunk type as string with 4 chars. diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index ffa3875057..234ed6bbd0 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/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.Shared.Return(currentChunk.Data.Array); + ArrayPool.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 /// The containing data. private void ReadHeaderChunk(ReadOnlySpan 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]); } /// @@ -1208,36 +1204,40 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The . /// - 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; } /// @@ -1314,21 +1314,19 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Calculates the length of the given chunk. /// - /// The chunk. /// /// Thrown if the input stream is not valid. /// - 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); } /// diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 0cded1d80c..17aae17620 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/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 /// /// The containing image data. /// The . - private void WriteHeaderChunk(Stream stream, PngHeader header) + private void WriteHeaderChunk(Stream stream, in PngHeader header) { BinaryPrimitives.WriteInt32BigEndian(new Span(this.chunkDataBuffer, 0, 4), header.Width); BinaryPrimitives.WriteInt32BigEndian(new Span(this.chunkDataBuffer, 4, 4), header.Height); @@ -436,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The containing image data. /// The . /// The quantized frame. - private void WritePaletteChunk(Stream stream, PngHeader header, QuantizedFrame quantized) + private void WritePaletteChunk(Stream stream, in PngHeader header, QuantizedFrame quantized) where TPixel : struct, IPixel { // Grab the palette and write it to the stream. diff --git a/src/ImageSharp/Formats/Png/PngHeader.cs b/src/ImageSharp/Formats/Png/PngHeader.cs index a70032ce3c..df85642bed 100644 --- a/src/ImageSharp/Formats/Png/PngHeader.cs +++ b/src/ImageSharp/Formats/Png/PngHeader.cs @@ -6,55 +6,73 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Represents the png header chunk. /// - 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; + } + /// - /// Gets or sets the dimension in x-direction of the image in pixels. + /// Gets the dimension in x-direction of the image in pixels. /// - public int Width { get; set; } + public int Width { get; } /// - /// Gets or sets the dimension in y-direction of the image in pixels. + /// Gets the dimension in y-direction of the image in pixels. /// - public int Height { get; set; } + public int Height { get; } /// - /// 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. /// - public byte BitDepth { get; set; } + public byte BitDepth { get; } /// - /// 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). /// - public PngColorType ColorType { get; set; } + public PngColorType ColorType { get; } /// - /// 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. /// - public byte CompressionMethod { get; set; } + public byte CompressionMethod { get; } /// - /// 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. /// - public byte FilterMethod { get; set; } + public byte FilterMethod { get; } /// - /// 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). /// - public PngInterlaceMode InterlaceMethod { get; set; } + public PngInterlaceMode InterlaceMethod { get; } } } diff --git a/src/ImageSharp/IImageFrameCollection.cs b/src/ImageSharp/IImageFrameCollection.cs index 81b512e221..c9232c7780 100644 --- a/src/ImageSharp/IImageFrameCollection.cs +++ b/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; diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 9061c334dc..3236e00072 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/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 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(Configuration config, Stream stream, Func action) diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 7d23d95d9c..294da3dc40 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/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 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 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); diff --git a/src/ImageSharp/Memory/SpanHelper.cs b/src/ImageSharp/Memory/SpanHelper.cs index 4a6b7b7ce6..592e2a885b 100644 --- a/src/ImageSharp/Memory/SpanHelper.cs +++ b/src/ImageSharp/Memory/SpanHelper.cs @@ -11,20 +11,6 @@ namespace SixLabors.ImageSharp.Memory /// internal static class SpanHelper { - /// - /// Copy 'count' number of elements of the same type from 'source' to 'dest' - /// - /// The element type. - /// The to copy elements from. - /// The destination . - /// The number of elements to copy - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ReadOnlySpan source, Span destination, int count) - where T : struct - { - source.Slice(0, count).CopyTo(destination); - } - /// /// Copy all elements of 'source' into 'destination'. /// @@ -35,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory public static void Copy(ReadOnlySpan source, Span destination) where T : struct { - Copy(source, destination, Math.Min(source.Length, destination.Length)); + source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination); } /// diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index c60aaecfba..e4f60e8b33 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/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. /// - public class ImageProperty : IEquatable + public readonly struct ImageProperty : IEquatable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// The name of the property. /// The value of the property. @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.MetaData } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the struct /// by making a copy from another property. /// /// @@ -71,11 +71,6 @@ namespace SixLabors.ImageSharp.MetaData /// 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 /// public override bool Equals(object obj) { - ImageProperty other = obj as ImageProperty; - - return this.Equals(other); + return obj is ImageProperty other && this.Equals(other); } /// @@ -155,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData /// An object to compare with this object. 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); } } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs index 8c3c1171c7..5bd38b195d 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs @@ -11,66 +11,66 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// /// Unknown /// - Unknown, + Unknown = 0, /// - /// Byte + /// An 8-bit unsigned integer. /// - Byte, + Byte = 1, /// - /// Ascii + /// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL. /// - Ascii, + Ascii = 2, /// - /// Short + /// A 16-bit (2-byte) unsigned integer. /// - Short, + Short = 3, /// - /// Long + /// A 32-bit (4-byte) unsigned integer. /// - Long, + Long = 4, /// - /// Rational + /// Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator. /// - Rational, + Rational = 5, /// - /// SignedByte + /// An 8-bit signed integer. /// - SignedByte, + SignedByte = 6, /// - /// Undefined + /// An 8-bit byte that can take any value depending on the field definition. /// - Undefined, + Undefined = 7, /// - /// SignedShort + /// A 16-bit (2-byte) signed integer. /// - SignedShort, + SignedShort = 8, /// - /// SignedLong + /// A 32-bit (4-byte) signed integer (2's complement notation). /// - SignedLong, + SignedLong = 9, /// - /// SignedRational + /// Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator. /// - SignedRational, + SignedRational = 10, /// - /// SingleFloat + /// A 32-bit floating point value. /// - SingleFloat, + SingleFloat = 11, /// - /// DoubleFloat + /// A 64-bit floating point value. /// - DoubleFloat + DoubleFloat = 12 } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index 7cd2d002d7..0f19083e53 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// /// The collection of EXIF values /// - private Collection values; + private List values; /// /// The list of invalid EXIF tags /// - private List invalidTags; + private IReadOnlyList invalidTags; /// /// The thumbnail offset position in the byte stream @@ -75,8 +75,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif this.invalidTags = new List(other.invalidTags); if (other.values != null) { - this.values = new Collection(); - foreach (ExifValue value in other.values) + this.values = new List(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 /// /// Gets or sets which parts will be written when the profile is added to an image. /// - public ExifParts Parts - { - get; - set; - } + public ExifParts Parts { get; set; } /// /// Gets the tags that where found but contained an invalid value. /// - public IEnumerable InvalidTags => this.invalidTags; + public IReadOnlyList InvalidTags => this.invalidTags; /// /// Gets the values of this EXIF profile. /// - public IEnumerable Values + public IReadOnlyList Values { get { @@ -163,6 +160,31 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return null; } + /// + /// Conditionally returns the value of the tag if it exists. + /// + /// The tag of the EXIF value. + /// The value of the tag, if found. + /// + /// The . + /// + 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; + } + /// /// Removes the value with the specified tag. /// @@ -193,16 +215,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// The value. public void SetValue(ExifTag tag, object value) { - foreach (ExifValue exifValue in this.Values) + for (int i = 0; i < this.Values.Count; i++) { - if (exifValue.Tag == tag) + if (this.values[i].Tag == tag) { - exifValue.Value = value; + this.values[i] = this.values[i].WithValue(value); + return; } } 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(); + this.values = new List(); 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(reader.InvalidTags); this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailLength = (int)reader.ThumbnailLength; diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 8a7b1f7d7e..c00eec6010 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/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 /// internal sealed class ExifReader { - private readonly Collection invalidTags = new Collection(); - private byte[] exifData; - private uint currentIndex; - private bool isLittleEndian; + private readonly List invalidTags = new List(); + 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(byte[] data); + public ExifReader(byte[] exifData) + { + DebugGuard.NotNull(exifData, nameof(exifData)); + + this.exifData = exifData; + } + + private delegate TDataType ConverterMethod(ReadOnlySpan data); /// /// Gets the invalid tags. /// - public IEnumerable InvalidTags => this.invalidTags; + public IReadOnlyList InvalidTags => this.invalidTags; /// /// Gets the thumbnail length in the byte stream /// - public uint ThumbnailLength - { - get; - private set; - } + public uint ThumbnailLength { get; private set; } /// /// Gets the thumbnail offset position in the byte stream /// - public uint ThumbnailOffset - { - get; - private set; - } + public uint ThumbnailOffset { get; private set; } /// /// 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; } } /// /// Reads and returns the collection of EXIF values. /// - /// The data. /// /// The . /// - public Collection Read(byte[] data) + public List ReadValues() { - DebugGuard.NotNull(data, nameof(data)); - - var result = new Collection(); + var values = new List(); - 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(ExifDataType dataType, byte[] data, ConverterMethod converter) + private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod 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 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 buffer) => buffer[0]; - private static string ToString(byte[] data) + private unsafe string ConvertToString(ReadOnlySpan 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 /// /// The values. /// The index. - private void AddValues(Collection values, uint index) + private void AddValues(List 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 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 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 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(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 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(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 span) + ? this.ConvertToUInt32(span) + : default; } - private ushort GetShort() + private ushort ReadUInt16() { - return this.ToShort(this.GetBytes(2)); + return this.TryReadSpan(2, out ReadOnlySpan 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 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(); + var values = new List(); 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 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 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 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 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 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 buffer) { - return unchecked((sbyte)data[0]); + return unchecked((sbyte)buffer[0]); } - private int ToSignedLong(byte[] data) + private int ConvertToInt32(ReadOnlySpan 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 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 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); } } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs new file mode 100644 index 0000000000..e497fc7fa4 --- /dev/null +++ b/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 + { + /// + /// The collection if Image File Directory tags + /// + 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 + }; + + /// + /// The collection of Exif tags + /// + 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 + }; + + /// + /// The collection of GPS tags + /// + 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 + }; + } +} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs index 3c2b23f37f..bdd902e239 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs @@ -13,11 +13,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// public sealed class ExifValue : IEquatable { - /// - /// The exif value. - /// - private object exifValue; - /// /// Initializes a new instance of the 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(); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The tag. - /// The data type. - /// Whether the value is an array. - 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 /// The value. /// Whether the value is an array. 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; } /// /// Gets the data type of the exif value. /// - public ExifDataType DataType - { - get; - } + public ExifDataType DataType { get; } /// /// Gets a value indicating whether the value is an array. /// - public bool IsArray - { - get; - } + public bool IsArray { get; } /// /// Gets the tag of the exif value. /// - public ExifTag Tag - { - get; - } + public ExifTag Tag { get; } /// - /// Gets or sets the value. + /// Gets the value. /// - public object Value - { - get - { - return this.exifValue; - } - - set - { - this.CheckValue(value); - this.exifValue = value; - } - } + public object Value { get; } /// /// 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 /// 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); } /// @@ -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); + } + + /// + /// Clones the current value, overwriting the value. + /// + /// The value to overwrite. + /// + public ExifValue WithValue(object value) + { + this.CheckValue(value); + + return new ExifValue(this.Tag, this.DataType, value, this.IsArray); } /// @@ -253,26 +218,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// 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; } /// @@ -635,29 +581,35 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// Returns an EXIF value with a numeric type for the given tag. /// /// The tag. - /// The numeric type. + /// The value. /// Whether the value is an array. /// /// The . /// - 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); } /// @@ -739,8 +691,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// The 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; } } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs index a4cfc7e13e..f7363ef314 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs +++ b/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 /// private const int StartIndex = 6; - /// - /// The collection if Image File Directory tags - /// - 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 - }; - - /// - /// The collection of Exif tags - /// - 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 - }; - - /// - /// The collection of GPS tags - /// - 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 - }; - /// /// Which parts will be written. /// private ExifParts allowedParts; - private Collection values; - private Collection dataOffsets; - private Collection ifdIndexes; - private Collection exifIndexes; - private Collection gpsIndexes; + private IList values; + private List dataOffsets; + private List ifdIndexes; + private List exifIndexes; + private List gpsIndexes; /// /// Initializes a new instance of the class. /// /// The values. /// The allowed parts. - public ExifWriter(Collection values, ExifParts allowedParts) + public ExifWriter(IList 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); } /// @@ -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 destination, int offset) + { + BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), *((int*)&value)); + + return offset + 4; + } + + private static unsafe int WriteDouble(double value, Span destination, int offset) + { + BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), *((long*)&value)); + + return offset + 8; + } + + private static int Write(ReadOnlySpan source, Span 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 indexes, ExifTag tag) + private static int WriteInt16(short value, Span destination, int offset) + { + BinaryPrimitives.WriteInt16LittleEndian(destination.Slice(offset, 2), value); + + return offset + 2; + } + + private static int WriteUInt16(ushort value, Span destination, int offset) + { + BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(offset, 2), value); + + return offset + 2; + } + + private static int WriteUInt32(uint value, Span destination, int offset) + { + BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(offset, 4), value); + + return offset + 4; + } + + private static int WriteInt32(int value, Span destination, int offset) + { + BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value); + + return offset + 4; + } + + private int GetIndex(IList indexes, ExifTag tag) { foreach (int index in indexes) { @@ -430,14 +202,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return newIndex; } - private Collection GetIndexes(ExifParts part, ExifTag[] tags) + private List GetIndexes(ExifParts part, ExifTag[] tags) { if (((int)this.allowedParts & (int)part) == 0) { - return new Collection(); + return new List(); } - Collection result = new Collection(); + var result = new List(); 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 indexes) + private uint GetLength(IList indexes) { uint length = 0; @@ -494,7 +266,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return newOffset; } - private int WriteData(Collection indexes, byte[] destination, int offset) + private int WriteData(List 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 indexes, byte[] destination, int offset) + private int WriteHeaders(List indexes, byte[] destination, int offset) { - this.dataOffsets = new Collection(); + this.dataOffsets = new List(); - 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 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 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 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(); } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs index 46aec49be6..ee8b4c731e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs +++ b/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); } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs index ae9cd84b47..8a7162198b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs +++ b/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); } /// diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs index 922926e385..57e2e984be 100644 --- a/src/ImageSharp/PixelFormats/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/Alpha8.cs @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// True if the object is equal to the packed vector. public override bool Equals(object obj) { - return (obj is Alpha8) && this.Equals((Alpha8)obj); + return obj is Alpha8 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/Bgr24.cs b/src/ImageSharp/PixelFormats/Bgr24.cs index e8fc24b31c..61b447b882 100644 --- a/src/ImageSharp/PixelFormats/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/Bgr24.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return obj?.GetType() == typeof(Bgr24) && this.Equals((Bgr24)obj); + return obj is Bgr24 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/Bgra32.cs index 2b9a447bd9..ad5efa2578 100644 --- a/src/ImageSharp/PixelFormats/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/Bgra32.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - 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); /// public override int GetHashCode() diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs index f339881d0c..0f52d00687 100644 --- a/src/ImageSharp/PixelFormats/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/Bgra4444.cs @@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is Bgra4444) && this.Equals((Bgra4444)obj); + return obj is Bgra4444 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs index ea1d7446d5..d24bab848e 100644 --- a/src/ImageSharp/PixelFormats/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/Bgra5551.cs @@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is Bgra5551) && this.Equals((Bgra5551)obj); + return obj is Bgra5551 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index c646d804ac..184928d0e4 100644 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ b/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 a that represents the color defined by the provided RGBA values. 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; } diff --git a/src/ImageSharp/PixelFormats/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs index f19d1ec952..c92de4ef13 100644 --- a/src/ImageSharp/PixelFormats/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/HalfVector4.cs @@ -191,7 +191,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is HalfVector4) && this.Equals((HalfVector4)obj); + return obj is HalfVector4 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index 6a2902f9b1..e27bde8822 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/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 constantsBytes = constants.AsSpan().NonPortableCast(); PixelOperations.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length); diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs index 69ed25e439..9bac828560 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is NormalizedByte2) && this.Equals((NormalizedByte2)obj); + return obj is NormalizedByte2 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs index 18af886dfe..a3aa60fd21 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is NormalizedByte4) && this.Equals((NormalizedByte4)obj); + return obj is NormalizedByte4 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs index 157f7419be..87c92cb73b 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs @@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is NormalizedShort4) && this.Equals((NormalizedShort4)obj); + return obj is NormalizedShort4 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/Rgb24.cs b/src/ImageSharp/PixelFormats/Rgb24.cs index bb31d9ae97..f07eadd56e 100644 --- a/src/ImageSharp/PixelFormats/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/Rgb24.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return obj?.GetType() == typeof(Rgb24) && this.Equals((Rgb24)obj); + return obj is Rgb24 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs index 0817ef5ad3..6a9f38d7ff 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats { diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs index 0234a48268..92faa4e27b 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.cs @@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is RgbaVector) && this.Equals((RgbaVector)obj); + return obj is RgbaVector other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs index 16151256df..75429c0afb 100644 --- a/src/ImageSharp/PixelFormats/Short2.cs +++ b/src/ImageSharp/PixelFormats/Short2.cs @@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is Short2) && this.Equals((Short2)obj); + return obj is Short2 other && this.Equals(other); } /// diff --git a/src/ImageSharp/PixelFormats/Short4.cs b/src/ImageSharp/PixelFormats/Short4.cs index 6eab376170..0ea2b10c29 100644 --- a/src/ImageSharp/PixelFormats/Short4.cs +++ b/src/ImageSharp/PixelFormats/Short4.cs @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public override bool Equals(object obj) { - return (obj is Short4) && this == (Short4)obj; + return obj is Short4 other && this.Equals(other); } /// diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index 1f459e7cb0..5f3defd118 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Primitives } /// - public override bool Equals(object obj) => obj is DenseMatrix matrix && this.Equals(matrix); + public override bool Equals(object obj) => obj is DenseMatrix other && this.Equals(other); /// public override int GetHashCode() => this.Data.GetHashCode(); diff --git a/src/ImageSharp/Primitives/LongRational.cs b/src/ImageSharp/Primitives/LongRational.cs index 641a1e5e53..d790b110d0 100644 --- a/src/ImageSharp/Primitives/LongRational.cs +++ b/src/ImageSharp/Primitives/LongRational.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Primitives /// /// This is a very simplified implementation of a rational number designed for use with metadata only. /// - internal struct LongRational : IEquatable + internal readonly struct LongRational : IEquatable { /// /// Initializes a new instance of the struct. @@ -26,126 +26,25 @@ namespace SixLabors.ImageSharp.Primitives /// The number below the line in a vulgar fraction; a divisor. /// public LongRational(long numerator, long denominator) - : this(numerator, denominator, false) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The number above the line in a vulgar fraction showing how many of the parts - /// indicated by the denominator are taken. - /// - /// - /// The number below the line in a vulgar fraction; a divisor. - /// - /// - /// Whether to attempt to simplify the fractional parts. - /// - public LongRational(long numerator, long denominator, bool simplify) - : this() { this.Numerator = numerator; this.Denominator = denominator; - - if (simplify) - { - this.Simplify(); - } - } - - /// - /// Initializes a new instance of the struct. - /// - /// The to create the instance from. - /// Whether to use the best possible precision when parsing the value. - 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(); } /// /// Gets the numerator of a number. /// - public long Numerator - { - get; - private set; - } + public long Numerator { get; } /// /// Gets the denominator of a number. /// - public long Denominator - { - get; - private set; - } + public long Denominator { get; } /// /// Gets a value indicating whether this instance is indeterminate. /// - public bool IsIndeterminate - { - get - { - if (this.Denominator != 0) - { - return false; - } - - return this.Numerator == 0; - } - } + public bool IsIndeterminate => this.Denominator == 0 && this.Numerator == 0; /// /// Gets a value indicating whether this instance is an integer (n, 1) @@ -155,76 +54,28 @@ namespace SixLabors.ImageSharp.Primitives /// /// Gets a value indicating whether this instance is equal to negative infinity (-1, 0) /// - public bool IsNegativeInfinity - { - get - { - if (this.Denominator != 0) - { - return false; - } - - return this.Numerator == -1; - } - } + public bool IsNegativeInfinity => this.Denominator == 0 && this.Numerator == -1; /// /// Gets a value indicating whether this instance is equal to positive infinity (1, 0) /// - public bool IsPositiveInfinity - { - get - { - if (this.Denominator != 0) - { - return false; - } - - return this.Numerator == 1; - } - } + public bool IsPositiveInfinity => this.Denominator == 0 && this.Numerator == 1; /// /// Gets a value indicating whether this instance is equal to 0 (0, 1) /// - public bool IsZero - { - get - { - if (this.Denominator != 1) - { - return false; - } - - return this.Numerator == 0; - } - } + public bool IsZero => this.Denominator == 1 && this.Numerator == 0; /// 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; } /// public override int GetHashCode() { - return this.GetHashCode(this); + return ((this.Numerator * 397) ^ this.Denominator).GetHashCode(); } /// @@ -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(); } /// - /// Finds the greatest common divisor of two values. + /// Create a new instance of the struct from a double value. /// - /// The first value - /// The second value - /// The - private static long GreatestCommonDivisor(long left, long right) + /// The to create the instance from. + /// Whether to use the best possible precision when parsing the value. + 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); + } - /// - /// Simplifies the - /// - 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(); + } + + /// + /// Finds the greatest common divisor of two values. + /// + /// The first value + /// The second value + /// The + private static long GreatestCommonDivisor(long left, long right) + { + return right == 0 ? left : GreatestCommonDivisor(right, left % right); + } + + /// + /// Simplifies the + /// + 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); } - } - /// - /// Returns the hash code for this instance. - /// - /// - /// The instance of to return the hash code for. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// - private int GetHashCode(LongRational rational) - { - return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode(); + return this; } } } \ No newline at end of file diff --git a/src/ImageSharp/Primitives/Rational.cs b/src/ImageSharp/Primitives/Rational.cs index 6678eb9540..b598f0e02f 100644 --- a/src/ImageSharp/Primitives/Rational.cs +++ b/src/ImageSharp/Primitives/Rational.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Primitives /// /// This is a very simplified implementation of a rational number designed for use with metadata only. /// - public struct Rational : IEquatable + public readonly struct Rational : IEquatable { /// /// Initializes a new instance of the struct. @@ -41,10 +41,18 @@ namespace SixLabors.ImageSharp.Primitives /// Specified if the rational should be simplified. 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; + } } /// @@ -63,7 +71,7 @@ namespace SixLabors.ImageSharp.Primitives /// Whether to use the best possible precision when parsing the value. 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 /// 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); } /// 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 /// 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 /// The 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); } } diff --git a/src/ImageSharp/Primitives/SignedRational.cs b/src/ImageSharp/Primitives/SignedRational.cs index c2a7b3e234..7e486e4f22 100644 --- a/src/ImageSharp/Primitives/SignedRational.cs +++ b/src/ImageSharp/Primitives/SignedRational.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Primitives /// /// This is a very simplified implementation of a rational number designed for use with metadata only. /// - public struct SignedRational : IEquatable + public readonly struct SignedRational : IEquatable { /// /// Initializes a new instance of the struct. @@ -41,10 +41,18 @@ namespace SixLabors.ImageSharp.Primitives /// Specified if the rational should be simplified. 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; + } } /// @@ -63,7 +71,7 @@ namespace SixLabors.ImageSharp.Primitives /// Whether to use the best possible precision when parsing the value. 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 /// 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); } /// 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 /// 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 /// The 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); } } diff --git a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index a813e0c1dd..908830fb7c 100644 --- a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var apSource = new Span(source, 1, source.Length - 1); var apDest = new Span(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(source, 1, source.Length - 1); var apDest = new Span(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(source, 1, source.Length - 1); var apDest = new Span(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(source, 1, source.Length - 1); var apDest = new Span(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(source, 1, source.Length - 1); var apDest = new Span(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(source); var apDest = new Span(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(source); var apDest = new Span(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); diff --git a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs index 5e15d556c0..b5886522a8 100644 --- a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs +++ b/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); } /// @@ -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); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index 65a469b6f1..d98c61279b 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/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(() => { value.Value = 15; }); + Assert.Throws(() => { 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(() => { value.Value = 75; }); + Assert.Throws(() => { 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(() => { value.Value = "ImageSharp"; }); + Assert.Throws(() => { 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 image = new Image(100, 100); + var image = new Image(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(() => 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 image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); Assert.NotNull(image); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs index dee6d5ff39..c9542a98a9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs +++ b/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 result = reader.Read(data); + IList 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 result = reader.Read(data); + IList result = reader.ReadValues(); Assert.Equal(0, result.Count); }