diff --git a/.gitmodules b/.gitmodules
index e7972649f4..37ef701cdf 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,3 +2,6 @@
path = tests/Images/External
url = https://github.com/SixLabors/Imagesharp.Tests.Images.git
branch = master
+[submodule "standards"]
+ path = standards
+ url = https://github.com/SixLabors/Standards
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 40282d8bcd..41117c7a8d 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
+ standards\.editorconfig = standards\.editorconfig
.travis.yml = .travis.yml
appveyor.yml = appveyor.yml
.github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md
@@ -14,12 +14,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
.github\CONTRIBUTING.md = .github\CONTRIBUTING.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
features.md = features.md
- ImageSharp.ruleset = ImageSharp.ruleset
ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings
NuGet.config = NuGet.config
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
README.md = README.md
run-tests.ps1 = run-tests.ps1
+ standards\SixLabors.ruleset = standards\SixLabors.ruleset
+ standards\stylecop.json = standards\stylecop.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index ae5069be1d..f4906d25e3 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -1,54 +1,53 @@
-
- An extension to ImageSharp that allows the drawing of images, paths, and text.
- SixLabors.ImageSharp.Drawing
- $(packageversion)
- 0.0.1
- SixLabors and contributors
- netstandard1.3;netstandard2.0
- 7.3
- true
- true
- SixLabors.ImageSharp.Drawing
- SixLabors.ImageSharp.Drawing
- Image Draw Shape Path Font
- https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png
- https://github.com/SixLabors/ImageSharp
- http://www.apache.org/licenses/LICENSE-2.0
- git
- https://github.com/SixLabors/ImageSharp
- false
- false
- false
- false
- false
- false
- false
- false
- false
- full
- portable
- True
-
-
-
-
-
-
-
-
-
-
-
-
- All
-
-
-
- ..\..\ImageSharp.ruleset
- SixLabors.ImageSharp
-
-
- true
-
+
+ SixLabors.ImageSharp.Drawing
+ SixLabors and contributors
+ Six Labors
+ Copyright (c) Six Labors and contributors.
+ SixLabors.ImageSharp
+ An extension to ImageSharp that allows the drawing of images, paths, and text.
+ en
+
+ $(packageversion)
+ 0.0.1
+ netstandard1.3;netstandard2.0
+ 7.3
+ true
+ true
+ SixLabors.ImageSharp.Drawing
+ SixLabors.ImageSharp.Drawing
+ Image Draw Shape Path Font
+ https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png
+ https://github.com/SixLabors/ImageSharp
+ http://www.apache.org/licenses/LICENSE-2.0
+ git
+ https://github.com/SixLabors/ImageSharp
+ full
+ portable
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\standards\SixLabors.ruleset
+ SixLabors.ImageSharp
+
+
+
+ true
+
\ No newline at end of file
diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
index 9e7624480d..eb6991e6a1 100644
--- a/src/ImageSharp/Advanced/AotCompilerTools.cs
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -1,6 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Numerics;
+using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -15,6 +18,17 @@ namespace SixLabors.ImageSharp.Advanced
///
public static class AotCompilerTools
{
+ static AotCompilerTools()
+ {
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ }
+
///
/// Seeds the compiler using the given pixel format.
///
@@ -27,6 +41,13 @@ namespace SixLabors.ImageSharp.Advanced
AotCompileWuQuantizer();
AotCompileDithering();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+
+ AotCodec(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder());
+ AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder());
+ AotCodec(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder());
+ AotCodec(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder());
+
// TODO: Do the discovery work to figure out what works and what doesn't.
}
@@ -99,5 +120,31 @@ namespace SixLabors.ImageSharp.Advanced
TPixel pixel = default;
test.Dither(new ImageFrame(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
}
+
+ ///
+ /// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS.
+ ///
+ /// The image decoder to seed.
+ /// The image encoder to seed.
+ /// The pixel format.
+ private static void AotCodec(IImageDecoder decoder, IImageEncoder encoder)
+ where TPixel : struct, IPixel
+ {
+ try
+ {
+ decoder.Decode(Configuration.Default, null);
+ }
+ catch
+ {
+ }
+
+ try
+ {
+ encoder.Encode(null, null);
+ }
+ catch
+ {
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs
index ea6df86e27..146acf12ee 100644
--- a/src/ImageSharp/ColorSpaces/CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/CieLab.cs
@@ -118,13 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.A.GetHashCode());
- hash = HashHelpers.Combine(hash, this.B.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs
index f1a7425e9e..074bc1516b 100644
--- a/src/ImageSharp/ColorSpaces/CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/CieLch.cs
@@ -122,10 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
public override int GetHashCode()
{
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.C.GetHashCode());
- hash = HashHelpers.Combine(hash, this.H.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
+ return HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
}
///
diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs
index 256b5dc0fd..ab6f639a2b 100644
--- a/src/ImageSharp/ColorSpaces/CieLchuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs
@@ -119,13 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.C.GetHashCode());
- hash = HashHelpers.Combine(hash, this.H.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs
index 8fe073d6bf..d54d92b62a 100644
--- a/src/ImageSharp/ColorSpaces/CieLuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLuv.cs
@@ -119,13 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.U.GetHashCode());
- hash = HashHelpers.Combine(hash, this.V.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
index f625bb7616..49c1da9f10 100644
--- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
+ public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
///
public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs
index 7137360e94..fff296945e 100644
--- a/src/ImageSharp/ColorSpaces/CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyy.cs
@@ -83,12 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.X.GetHashCode();
- hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
- return HashHelpers.Combine(hash, this.Yl.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Yl);
///
public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs
index c0ed356601..30521476e5 100644
--- a/src/ImageSharp/ColorSpaces/CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyz.cs
@@ -86,12 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
///
- public override int GetHashCode()
- {
- int hash = this.X.GetHashCode();
- hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
- return HashHelpers.Combine(hash, this.Z.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
///
public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs
index 634667c0c6..04901126c1 100644
--- a/src/ImageSharp/ColorSpaces/Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Cmyk.cs
@@ -90,13 +90,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.C.GetHashCode();
- hash = HashHelpers.Combine(hash, this.M.GetHashCode());
- hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
- return HashHelpers.Combine(hash, this.K.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.C, this.M, this.Y, this.K);
///
public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
index 68b4d95fc6..4c69133e0f 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
@@ -86,14 +86,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- int hashCode = this.R.GetHashCode();
- hashCode = (hashCode * 397) ^ this.G.GetHashCode();
- return (hashCode * 397) ^ this.B.GetHashCode();
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
index 73aa60b6cb..639d0b2933 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
@@ -57,10 +58,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
///
- public override int GetHashCode()
- {
- int hash = base.GetHashCode();
- return HashHelpers.Combine(hash, this.Gamma.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(
+ this.WhitePoint,
+ this.ChromaticityCoordinates,
+ this.Gamma);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
index 5a89321c8f..6051f52865 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
+
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
///
@@ -76,8 +78,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
///
public override int GetHashCode()
{
- int hash = this.WhitePoint.GetHashCode();
- return HashHelpers.Combine(hash, this.ChromaticityCoordinates.GetHashCode());
+ return HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs
index f6e531df35..04b3bea41f 100644
--- a/src/ImageSharp/ColorSpaces/Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Hsl.cs
@@ -84,12 +84,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.H.GetHashCode();
- hash = HashHelpers.Combine(hash, this.S.GetHashCode());
- return HashHelpers.Combine(hash, this.L.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L);
///
public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs
index 631f03d09f..8ccc74ae09 100644
--- a/src/ImageSharp/ColorSpaces/Hsv.cs
+++ b/src/ImageSharp/ColorSpaces/Hsv.cs
@@ -82,12 +82,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.H.GetHashCode();
- hash = HashHelpers.Combine(hash, this.S.GetHashCode());
- return HashHelpers.Combine(hash, this.V.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V);
///
public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs
index f4fa29d314..dcae65e425 100644
--- a/src/ImageSharp/ColorSpaces/HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/HunterLab.cs
@@ -119,10 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.A.GetHashCode());
- hash = HashHelpers.Combine(hash, this.B.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
+ return HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
}
///
diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs
index ec6d18be2b..46b2275968 100644
--- a/src/ImageSharp/ColorSpaces/LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs
@@ -126,12 +126,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.R.GetHashCode();
- hash = HashHelpers.Combine(hash, this.G.GetHashCode());
- return HashHelpers.Combine(hash, this.B.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
///
public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs
index 0a8b7aa7b9..0ee56abbc2 100644
--- a/src/ImageSharp/ColorSpaces/Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Lms.cs
@@ -87,12 +87,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.M.GetHashCode());
- return HashHelpers.Combine(hash, this.S.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S);
///
public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs
index 97fafbaf37..cdc9e90ad4 100644
--- a/src/ImageSharp/ColorSpaces/Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Rgb.cs
@@ -147,12 +147,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
///
- public override int GetHashCode()
- {
- int hash = this.R.GetHashCode();
- hash = HashHelpers.Combine(hash, this.G.GetHashCode());
- return HashHelpers.Combine(hash, this.B.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
///
public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
index 6aa191c2de..b0563bb899 100644
--- a/src/ImageSharp/ColorSpaces/YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/YCbCr.cs
@@ -83,12 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.Y.GetHashCode();
- hash = HashHelpers.Combine(hash, this.Cb.GetHashCode());
- return HashHelpers.Combine(hash, this.Cr.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr);
///
public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})");
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
index 9aeb209319..83216aaa72 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Common/Helpers/TestHelpers.cs b/src/ImageSharp/Common/Helpers/TestHelpers.cs
index fd16a577ab..d330233c4c 100644
--- a/src/ImageSharp/Common/Helpers/TestHelpers.cs
+++ b/src/ImageSharp/Common/Helpers/TestHelpers.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Common.Helpers
diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs
index 5347efcc09..bef7eb1610 100644
--- a/src/ImageSharp/Common/Helpers/TolerantMath.cs
+++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs
@@ -16,6 +16,13 @@ namespace SixLabors.ImageSharp
private readonly double negEpsilon;
+ ///
+ /// A read-only default instance for using 1e-8 as epsilon.
+ /// It is a field so it can be passed as an 'in' parameter.
+ /// Does not necessarily fit all use cases!
+ ///
+ public static readonly TolerantMath Default = new TolerantMath(1e-8);
+
public TolerantMath(double epsilon)
{
DebugGuard.MustBeGreaterThan(epsilon, 0, nameof(epsilon));
@@ -24,8 +31,6 @@ namespace SixLabors.ImageSharp
this.negEpsilon = -epsilon;
}
- public static TolerantMath Default { get; } = new TolerantMath(1e-8);
-
///
/// == 0
///
diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs
index 75bb00b6a5..5c122217d1 100644
--- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs
+++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs
@@ -5,6 +5,7 @@ using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp
{
@@ -70,5 +71,41 @@ namespace SixLabors.ImageSharp
UnPremultiply(ref v);
}
}
+
+ ///
+ /// Transforms a vector by the given matrix.
+ ///
+ /// The source vector.
+ /// The transformation matrix.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
+ {
+ float x = vector.X;
+ float y = vector.Y;
+ float z = vector.Z;
+ float w = vector.W;
+
+ vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51;
+ vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52;
+ vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53;
+ vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54;
+ }
+
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ /// The transformation matrix.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Transform(Span vectors, ref ColorMatrix matrix)
+ {
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+
+ for (int i = 0; i < vectors.Length; i++)
+ {
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
+ Transform(ref v, ref matrix);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
index 0b45719c38..40163bc789 100644
--- a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
+++ b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Threading.Tasks;
diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
index 1d1734a863..a930b8390f 100644
--- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
+++ b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action> body)
- where T : struct
+ where T : unmanaged
{
int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask);
@@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
Rectangle rectangle,
Configuration configuration,
Action> body)
- where T : struct
+ where T : unmanaged
{
IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body);
}
diff --git a/src/ImageSharp/Common/Tuples/Octet.cs b/src/ImageSharp/Common/Tuples/Octet.cs
index 539b74e324..23faf08ae9 100644
--- a/src/ImageSharp/Common/Tuples/Octet.cs
+++ b/src/ImageSharp/Common/Tuples/Octet.cs
@@ -1,4 +1,7 @@
-using System.Runtime.CompilerServices;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Tuples
diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs
index cae283d628..b3a32deeef 100644
--- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs
+++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs
@@ -1,4 +1,7 @@
-using System.Numerics;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -37,12 +40,11 @@ namespace SixLabors.ImageSharp.Tuples
this.B += other.B;
}
- ///
- /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4!
- /// TODO: Move it somewhere else.
+ /// .
+ /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void RoundAndDownscalePreAvx2()
+ internal void RoundAndDownscalePreAvx2(float downscaleFactor)
{
ref Vector a = ref Unsafe.As>(ref this.A);
a = a.FastRound();
@@ -50,8 +52,8 @@ namespace SixLabors.ImageSharp.Tuples
ref Vector b = ref Unsafe.As>(ref this.B);
b = b.FastRound();
- // Downscale by 1/255
- var scale = new Vector4(1 / 255f);
+ // Downscale by 1/factor
+ var scale = new Vector4(1 / downscaleFactor);
this.A *= scale;
this.B *= scale;
}
@@ -61,14 +63,14 @@ namespace SixLabors.ImageSharp.Tuples
/// TODO: Move it somewhere else.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void RoundAndDownscaleAvx2()
+ internal void RoundAndDownscaleAvx2(float downscaleFactor)
{
ref Vector self = ref Unsafe.As>(ref this);
Vector v = self;
v = v.FastRound();
- // Downscale by 1/255
- v *= new Vector(1 / 255f);
+ // Downscale by 1/factor
+ v *= new Vector(1 / downscaleFactor);
self = v;
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
index ef063f0106..5f14d22436 100644
--- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
@@ -25,7 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// If the first byte is zero, the record has different meanings, depending
/// on the second byte. If the second byte is zero, it is the end of the row,
/// if it is one, it is the end of the image.
- /// Not supported at the moment.
///
RLE8 = 1,
@@ -42,7 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// Each image row has a multiple of four elements. If the
/// row has less elements, zeros will be added at the right side.
- /// Not supported at the moment.
///
BitFields = 3,
diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs
index 99799b619c..5cbed4af2b 100644
--- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs
@@ -19,5 +19,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The list of file extensions that equate to a bmp.
///
public static readonly IEnumerable FileExtensions = new[] { "bm", "bmp", "dip" };
+
+ ///
+ /// Valid magic bytes markers identifying a Bitmap file.
+ ///
+ internal static class TypeMarkers
+ {
+ ///
+ /// Single-image BMP file that may have been created under Windows or OS/2.
+ ///
+ public const int Bitmap = 0x4D42;
+
+ ///
+ /// OS/2 Bitmap Array.
+ ///
+ public const int BitmapArray = 0x4142;
+
+ ///
+ /// OS/2 Color Icon.
+ ///
+ public const int ColorIcon = 0x4943;
+
+ ///
+ /// OS/2 Color Pointer.
+ ///
+ public const int ColorPointer = 0x5043;
+
+ ///
+ /// OS/2 Icon.
+ ///
+ public const int Icon = 0x4349;
+
+ ///
+ /// OS/2 Pointer.
+ ///
+ public const int Pointer = 0x5450;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 3d079cf619..82af2a671e 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// - JPG
/// - PNG
/// - RLE4
- /// - RLE8
- /// - BitFields
///
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index ef3ca24ee8..6bfdfa3a29 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.Buffers.Binary;
using System.IO;
+using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory;
@@ -14,7 +16,7 @@ using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Bmp
{
///
- /// Performs the bmp decoding operation.
+ /// Performs the bitmap decoding operation.
///
///
/// A useful decoding source example can be found at
@@ -22,19 +24,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal sealed class BmpDecoderCore
{
///
- /// The mask for the red part of the color for 16 bit rgb bitmaps.
+ /// The default mask for the red part of the color for 16 bit rgb bitmaps.
///
- private const int Rgb16RMask = 0x7C00;
+ private const int DefaultRgb16RMask = 0x7C00;
///
- /// The mask for the green part of the color for 16 bit rgb bitmaps.
+ /// The default mask for the green part of the color for 16 bit rgb bitmaps.
///
- private const int Rgb16GMask = 0x3E0;
+ private const int DefaultRgb16GMask = 0x3E0;
///
- /// The mask for the blue part of the color for 16 bit rgb bitmaps.
+ /// The default mask for the blue part of the color for 16 bit rgb bitmaps.
///
- private const int Rgb16BMask = 0x1F;
+ private const int DefaultRgb16BMask = 0x1F;
///
/// RLE8 flag value that indicates following byte has special meaning.
@@ -62,10 +64,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private Stream stream;
///
- /// The metadata
+ /// The metadata.
///
private ImageMetaData metaData;
+ ///
+ /// The bmp specific metadata.
+ ///
+ private BmpMetaData bmpMetaData;
+
///
/// The file header containing general information.
/// TODO: Why is this not used? We advance the stream but do not use the values parsed.
@@ -85,7 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Initializes a new instance of the class.
///
/// The configuration.
- /// The options
+ /// The options.
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
@@ -108,7 +115,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
try
{
- this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
+ int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);
@@ -119,7 +126,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
- this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ if (this.bmpMetaData.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
+ {
+ this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ }
+ else
+ {
+ this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ }
}
else if (this.infoHeader.BitsPerPixel == 24)
{
@@ -137,16 +151,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
+ bytesPerColorMapEntry,
inverted);
}
break;
case BmpCompression.RLE8:
- this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ case BmpCompression.RLE4:
+ this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
+
+ break;
+
+ case BmpCompression.BitFields:
+ this.ReadBitFields(pixels, inverted);
break;
+
default:
- throw new NotSupportedException("Does not support this kind of bitmap files.");
+ BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files.");
+
+ break;
}
return image;
@@ -198,30 +222,66 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Performs final shifting from a 5bit value to an 8bit one.
+ /// Decodes a bitmap containing BITFIELDS Compression type. For each color channel, there will be bitmask
+ /// which will be used to determine which bits belong to that channel.
///
- /// The masked and shifted value
- /// The
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2));
+ /// The pixel format.
+ /// The output pixel buffer containing the decoded image.
+ /// Whether the bitmap is inverted.
+ private void ReadBitFields(Buffer2D pixels, bool inverted)
+ where TPixel : struct, IPixel
+ {
+ if (this.infoHeader.BitsPerPixel == 16)
+ {
+ this.ReadRgb16(
+ pixels,
+ this.infoHeader.Width,
+ this.infoHeader.Height,
+ inverted,
+ this.infoHeader.RedMask,
+ this.infoHeader.GreenMask,
+ this.infoHeader.BlueMask);
+ }
+ else
+ {
+ this.ReadRgb32BitFields(
+ pixels,
+ this.infoHeader.Width,
+ this.infoHeader.Height,
+ inverted,
+ this.infoHeader.RedMask,
+ this.infoHeader.GreenMask,
+ this.infoHeader.BlueMask,
+ this.infoHeader.AlphaMask);
+ }
+ }
///
- /// Looks up color values and builds the image from de-compressed RLE8 data.
+ /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data.
/// Compressed RLE8 stream is uncompressed by
+ /// Compressed RLE4 stream is uncompressed by
///
/// The pixel format.
+ /// The compression type. Either RLE4 or RLE8.
/// The to assign the palette to.
/// The containing the colors.
/// The width of the bitmap.
/// The height of the bitmap.
/// Whether the bitmap is inverted.
- private void ReadRle8(Buffer2D pixels, byte[] colors, int width, int height, bool inverted)
+ private void ReadRle(BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : struct, IPixel
{
TPixel color = default;
using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean))
{
- this.UncompressRle8(width, buffer.GetSpan());
+ if (compression == BmpCompression.RLE8)
+ {
+ this.UncompressRle8(width, buffer.GetSpan());
+ }
+ else
+ {
+ this.UncompressRle4(width, buffer.GetSpan());
+ }
for (int y = 0; y < height; y++)
{
@@ -239,12 +299,122 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Produce uncompressed bitmap data from RLE8 stream
+ /// Produce uncompressed bitmap data from a RLE4 stream.
///
///
- /// RLE8 is a 2-byte run-length encoding
- ///
If first byte is 0, the second byte may have special meaning
- ///
Otherwise, first byte is the length of the run and second byte is the color for the run
+ /// RLE4 is a 2-byte run-length encoding.
+ ///
If first byte is 0, the second byte may have special meaning.
+ ///
Otherwise, the first byte is the length of the run and second byte contains two color indexes.
+ ///
+ /// The width of the bitmap.
+ /// Buffer for uncompressed data.
+ private void UncompressRle4(int w, Span buffer)
+ {
+#if NETCOREAPP2_1
+ Span cmd = stackalloc byte[2];
+#else
+ byte[] cmd = new byte[2];
+#endif
+ int count = 0;
+
+ while (count < buffer.Length)
+ {
+ if (this.stream.Read(cmd, 0, cmd.Length) != 2)
+ {
+ BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap.");
+ }
+
+ if (cmd[0] == RleCommand)
+ {
+ switch (cmd[1])
+ {
+ case RleEndOfBitmap:
+ return;
+
+ case RleEndOfLine:
+ int extra = count % w;
+ if (extra > 0)
+ {
+ count += w - extra;
+ }
+
+ break;
+
+ case RleDelta:
+ int dx = this.stream.ReadByte();
+ int dy = this.stream.ReadByte();
+ count += (w * dy) + dx;
+
+ break;
+
+ default:
+ // If the second byte > 2, we are in 'absolute mode'.
+ // The second byte contains the number of color indexes that follow.
+ int max = cmd[1];
+ int bytesToRead = (max + 1) / 2;
+
+ byte[] run = new byte[bytesToRead];
+
+ this.stream.Read(run, 0, run.Length);
+
+ int idx = 0;
+ for (int i = 0; i < max; i++)
+ {
+ byte twoPixels = run[idx];
+ if (i % 2 == 0)
+ {
+ byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
+ buffer[count++] = leftPixel;
+ }
+ else
+ {
+ byte rightPixel = (byte)(twoPixels & 0xF);
+ buffer[count++] = rightPixel;
+ idx++;
+ }
+ }
+
+ // Absolute mode data is aligned to two-byte word-boundary
+ int padding = bytesToRead & 1;
+
+ this.stream.Skip(padding);
+
+ break;
+ }
+ }
+ else
+ {
+ int max = cmd[0];
+
+ // The second byte contains two color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
+ byte twoPixels = cmd[1];
+ byte rightPixel = (byte)(twoPixels & 0xF);
+ byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
+
+ for (int idx = 0; idx < max; idx++)
+ {
+ if (idx % 2 == 0)
+ {
+ buffer[count] = leftPixel;
+ }
+ else
+ {
+ buffer[count] = rightPixel;
+ }
+
+ count++;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Produce uncompressed bitmap data from a RLE8 stream.
+ ///
+ ///
+ /// RLE8 is a 2-byte run-length encoding.
+ ///
If first byte is 0, the second byte may have special meaning.
+ ///
Otherwise, the first byte is the length of the run and second byte is the color for the run.
///
/// The width of the bitmap.
/// Buffer for uncompressed data.
@@ -261,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
{
- throw new Exception("Failed to read 2 bytes from stream");
+ BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap.");
}
if (cmd[0] == RleCommand)
@@ -329,18 +499,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The containing the colors.
/// The width of the bitmap.
/// The height of the bitmap.
- /// The number of bits per pixel.
+ /// The number of bits per pixel.
+ /// Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
+ /// the bytes per color palette entry's can be 3 bytes instead of 4.
/// Whether the bitmap is inverted.
- private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bits, bool inverted)
+ private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted)
where TPixel : struct, IPixel
{
// Pixels per byte (bits per pixel)
- int ppb = 8 / bits;
+ int ppb = 8 / bitsPerPixel;
int arrayWidth = (width + ppb - 1) / ppb;
// Bit mask
- int mask = 0xFF >> (8 - bits);
+ int mask = 0xFF >> (8 - bitsPerPixel);
// Rows are aligned on 4 byte boundaries
int padding = arrayWidth % 4;
@@ -366,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int colOffset = x * ppb;
for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++)
{
- int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
+ int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry;
color.FromBgr24(Unsafe.As(ref colors[colorIndex]));
pixelRow[newX] = color;
@@ -379,20 +551,32 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Reads the 16 bit color palette from the stream
+ /// Reads the 16 bit color palette from the stream.
///
/// The pixel format.
/// The to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
/// Whether the bitmap is inverted.
- private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted)
+ /// The bitmask for the red channel.
+ /// The bitmask for the green channel.
+ /// The bitmask for the blue channel.
+ private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask)
where TPixel : struct, IPixel
{
int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding;
TPixel color = default;
+ int rightShiftRedMask = CalculateRightShift((uint)redMask);
+ int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
+ int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
+
+ // Each color channel contains either 5 or 6 Bits values.
+ int redMaskBits = CountBits((uint)redMask);
+ int greenMaskBits = CountBits((uint)greenMask);
+ int blueMaskBits = CountBits((uint)blueMask);
+
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
@@ -406,10 +590,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
short temp = BitConverter.ToInt16(buffer.Array, offset);
- var rgb = new Rgb24(
- GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10),
- GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5),
- GetBytesFrom5BitValue(temp & Rgb16BMask));
+ // Rescale values, so the values range from 0 to 255.
+ int r = (redMaskBits == 5) ? GetBytesFrom5BitValue((temp & redMask) >> rightShiftRedMask) : GetBytesFrom6BitValue((temp & redMask) >> rightShiftRedMask);
+ int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask);
+ int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask);
+ var rgb = new Rgb24((byte)r, (byte)g, (byte)b);
color.FromRgb24(rgb);
pixelRow[x] = color;
@@ -420,7 +605,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Reads the 24 bit color palette from the stream
+ /// Performs final shifting from a 5bit value to an 8bit one.
+ ///
+ /// The masked and shifted value.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2));
+
+ ///
+ /// Performs final shifting from a 6bit value to an 8bit one.
+ ///
+ /// The masked and shifted value.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static byte GetBytesFrom6BitValue(int value) => (byte)((value << 2) | (value >> 4));
+
+ ///
+ /// Reads the 24 bit color palette from the stream.
///
/// The pixel format.
/// The to assign the palette to.
@@ -449,14 +650,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Reads the 32 bit color palette from the stream
+ /// Reads the 32 bit color palette from the stream.
///
/// The pixel format.
/// The to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
/// Whether the bitmap is inverted.
- private void ReadRgb32(Buffer2D pixels, int width, int height, bool inverted)
+ private void ReadRgb32Fast(Buffer2D pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel
{
int padding = CalculatePadding(width, 4);
@@ -477,6 +678,228 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
+ ///
+ /// Reads the 32 bit color palette from the stream, checking the alpha component of each pixel.
+ /// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format.
+ ///
+ /// The pixel format.
+ /// The to assign the palette to.
+ /// The width of the bitmap.
+ /// The height of the bitmap.
+ /// Whether the bitmap is inverted.
+ private void ReadRgb32Slow(Buffer2D pixels, int width, int height, bool inverted)
+ where TPixel : struct, IPixel
+ {
+ int padding = CalculatePadding(width, 4);
+
+ using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, padding))
+ using (IMemoryOwner bgraRow = this.memoryAllocator.Allocate(width))
+ {
+ Span bgraRowSpan = bgraRow.GetSpan();
+ long currentPosition = this.stream.Position;
+ bool hasAlpha = false;
+
+ // Loop though the rows checking each pixel. We start by assuming it's
+ // an BGR0 image. If we hit a non-zero alpha value, then we know it's
+ // actually a BGRA image, and change tactics accordingly.
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(row);
+
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ bgraRowSpan,
+ width);
+
+ // Check each pixel in the row to see if it has an alpha value.
+ for (int x = 0; x < width; x++)
+ {
+ Bgra32 bgra = bgraRowSpan[x];
+ if (bgra.A > 0)
+ {
+ hasAlpha = true;
+ break;
+ }
+ }
+
+ if (hasAlpha)
+ {
+ break;
+ }
+ }
+
+ // Reset our stream for a second pass.
+ this.stream.Position = currentPosition;
+
+ // Process the pixels in bulk taking the raw alpha component value.
+ if (hasAlpha)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(row);
+
+ int newY = Invert(y, height, inverted);
+ Span pixelSpan = pixels.GetRowSpan(newY);
+
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ pixelSpan,
+ width);
+ }
+
+ return;
+ }
+
+ // Slow path. We need to set each alpha component value to fully opaque.
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(row);
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ bgraRowSpan,
+ width);
+
+ int newY = Invert(y, height, inverted);
+ Span pixelSpan = pixels.GetRowSpan(newY);
+
+ for (int x = 0; x < width; x++)
+ {
+ Bgra32 bgra = bgraRowSpan[x];
+ bgra.A = byte.MaxValue;
+ ref TPixel pixel = ref pixelSpan[x];
+ pixel.FromBgra32(bgra);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Decode an 32 Bit Bitmap containing a bitmask for each color channel.
+ ///
+ /// The pixel format.
+ /// The output pixel buffer containing the decoded image.
+ /// The width of the image.
+ /// The height of the image.
+ /// Whether the bitmap is inverted.
+ /// The bitmask for the red channel.
+ /// The bitmask for the green channel.
+ /// The bitmask for the blue channel.
+ /// The bitmask for the alpha channel.
+ private void ReadRgb32BitFields(Buffer2D pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask)
+ where TPixel : struct, IPixel
+ {
+ TPixel color = default;
+ int padding = CalculatePadding(width, 4);
+ int stride = (width * 4) + padding;
+
+ int rightShiftRedMask = CalculateRightShift((uint)redMask);
+ int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
+ int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
+ int rightShiftAlphaMask = CalculateRightShift((uint)alphaMask);
+
+ int bitsRedMask = CountBits((uint)redMask);
+ int bitsGreenMask = CountBits((uint)greenMask);
+ int bitsBlueMask = CountBits((uint)blueMask);
+ int bitsAlphaMask = CountBits((uint)alphaMask);
+ float invMaxValueRed = 1.0f / (0xFFFFFFFF >> (32 - bitsRedMask));
+ float invMaxValueGreen = 1.0f / (0xFFFFFFFF >> (32 - bitsGreenMask));
+ float invMaxValueBlue = 1.0f / (0xFFFFFFFF >> (32 - bitsBlueMask));
+ uint maxValueAlpha = 0xFFFFFFFF >> (32 - bitsAlphaMask);
+ float invMaxValueAlpha = 1.0f / maxValueAlpha;
+
+ bool unusualBitMask = false;
+ if (bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8)
+ {
+ unusualBitMask = true;
+ }
+
+ using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
+ {
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(buffer.Array, 0, stride);
+ int newY = Invert(y, height, inverted);
+ Span pixelRow = pixels.GetRowSpan(newY);
+
+ int offset = 0;
+ for (int x = 0; x < width; x++)
+ {
+ uint temp = BitConverter.ToUInt32(buffer.Array, offset);
+
+ if (unusualBitMask)
+ {
+ uint r = (uint)(temp & redMask) >> rightShiftRedMask;
+ uint g = (uint)(temp & greenMask) >> rightShiftGreenMask;
+ uint b = (uint)(temp & blueMask) >> rightShiftBlueMask;
+ float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f;
+ var vector4 = new Vector4(
+ r * invMaxValueRed,
+ g * invMaxValueGreen,
+ b * invMaxValueBlue,
+ alpha);
+ color.FromVector4(vector4);
+ }
+ else
+ {
+ byte r = (byte)((temp & redMask) >> rightShiftRedMask);
+ byte g = (byte)((temp & greenMask) >> rightShiftGreenMask);
+ byte b = (byte)((temp & blueMask) >> rightShiftBlueMask);
+ byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : (byte)255;
+ color.FromRgba32(new Rgba32(r, g, b, a));
+ }
+
+ pixelRow[x] = color;
+ offset += 4;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Calculates the necessary right shifts for a given color bitmask (the 0 bits to the right).
+ ///
+ /// The color bit mask.
+ /// Number of bits to shift right.
+ private static int CalculateRightShift(uint n)
+ {
+ int count = 0;
+ while (n > 0)
+ {
+ if ((1 & n) == 0)
+ {
+ count++;
+ }
+ else
+ {
+ break;
+ }
+
+ n >>= 1;
+ }
+
+ return count;
+ }
+
+ ///
+ /// Counts none zero bits.
+ ///
+ /// A color mask.
+ /// The none zero bits.
+ private static int CountBits(uint n)
+ {
+ int count = 0;
+ while (n != 0)
+ {
+ count++;
+ n &= n - 1;
+ }
+
+ return count;
+ }
+
///
/// Reads the from the stream.
///
@@ -492,7 +915,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize < BmpInfoHeader.CoreSize)
{
- throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
+ BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'.");
}
int skipAmount = 0;
@@ -505,19 +928,64 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// read the rest of the header
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
+ BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2;
if (headerSize == BmpInfoHeader.CoreSize)
{
// 12 bytes
+ infoHeaderType = BmpInfoHeaderType.WinVersion2;
this.infoHeader = BmpInfoHeader.ParseCore(buffer);
}
- else if (headerSize >= BmpInfoHeader.Size)
+ else if (headerSize == BmpInfoHeader.Os22ShortSize)
{
- // >= 40 bytes
- this.infoHeader = BmpInfoHeader.Parse(buffer);
+ // 16 bytes
+ infoHeaderType = BmpInfoHeaderType.Os2Version2Short;
+ this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer);
+ }
+ else if (headerSize == BmpInfoHeader.SizeV3)
+ {
+ // == 40 bytes
+ infoHeaderType = BmpInfoHeaderType.WinVersion3;
+ this.infoHeader = BmpInfoHeader.ParseV3(buffer);
+
+ // if the info header is BMP version 3 and the compression type is BITFIELDS,
+ // color masks for each color channel follow the info header.
+ if (this.infoHeader.Compression == BmpCompression.BitFields)
+ {
+ byte[] bitfieldsBuffer = new byte[12];
+ this.stream.Read(bitfieldsBuffer, 0, 12);
+ Span data = bitfieldsBuffer.AsSpan();
+ this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
+ this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
+ this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4));
+ }
+ }
+ else if (headerSize == BmpInfoHeader.AdobeV3Size)
+ {
+ // == 52 bytes
+ infoHeaderType = BmpInfoHeaderType.AdobeVersion3;
+ this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: false);
+ }
+ else if (headerSize == BmpInfoHeader.AdobeV3WithAlphaSize)
+ {
+ // == 56 bytes
+ infoHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha;
+ this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: true);
+ }
+ else if (headerSize == BmpInfoHeader.Os2v2Size)
+ {
+ // == 64 bytes
+ infoHeaderType = BmpInfoHeaderType.Os2Version2;
+ this.infoHeader = BmpInfoHeader.ParseOs2Version2(buffer);
+ }
+ else if (headerSize >= BmpInfoHeader.SizeV4)
+ {
+ // >= 108 bytes
+ infoHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5;
+ this.infoHeader = BmpInfoHeader.ParseV4(buffer);
}
else
{
- throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
+ BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{headerSize}'.");
}
// Resolution is stored in PPM.
@@ -540,13 +1008,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.metaData = meta;
short bitsPerPixel = this.infoHeader.BitsPerPixel;
- BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance);
+ this.bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance);
+ this.bmpMetaData.InfoHeaderType = infoHeaderType;
// We can only encode at these bit rates so far.
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24)
|| bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32))
{
- bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
+ this.bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
}
// skip the remaining header because we can't read those parts
@@ -566,12 +1035,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer);
+
+ if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
+ {
+ BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'.");
+ }
}
///
/// Reads the and from the stream and sets the corresponding fields.
///
- private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
+ /// Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
+ /// the bytes per color palette entry's can be 3 bytes instead of 4.
+ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
this.stream = stream;
@@ -591,6 +1067,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
int colorMapSize = -1;
+ int bytesPerColorMapEntry = 4;
if (this.infoHeader.ClrUsed == 0)
{
@@ -598,12 +1075,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
|| this.infoHeader.BitsPerPixel == 4
|| this.infoHeader.BitsPerPixel == 8)
{
- colorMapSize = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel) * 4;
+ int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize;
+ int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel);
+ bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth;
+ colorMapSize = colorMapSizeBytes;
}
}
else
{
- colorMapSize = this.infoHeader.ClrUsed * 4;
+ colorMapSize = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
}
palette = null;
@@ -613,7 +1093,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// 256 * 4
if (colorMapSize > 1024)
{
- throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
+ BmpThrowHelper.ThrowImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
@@ -622,6 +1102,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
this.infoHeader.VerifyDimensions();
+
+ return bytesPerColorMapEntry;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index a67c581eb0..27a38bc0d1 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -23,6 +23,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private int padding;
+ ///
+ /// The mask for the alpha channel of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32AlphaMask = 0xFF << 24;
+
+ ///
+ /// The mask for the red part of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32RedMask = 0xFF << 16;
+
+ ///
+ /// The mask for the green part of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32GreenMask = 0xFF << 8;
+
+ ///
+ /// The mask for the blue part of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32BlueMask = 0xFF;
+
private readonly MemoryAllocator memoryAllocator;
private Configuration configuration;
@@ -92,8 +112,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
+ int infoHeaderSize = BmpInfoHeader.SizeV4;
var infoHeader = new BmpInfoHeader(
- headerSize: BmpInfoHeader.Size,
+ headerSize: infoHeaderSize,
height: image.Height,
width: image.Width,
bitsPerPixel: bpp,
@@ -102,26 +123,37 @@ namespace SixLabors.ImageSharp.Formats.Bmp
clrUsed: 0,
clrImportant: 0,
xPelsPerMeter: hResolution,
- yPelsPerMeter: vResolution);
+ yPelsPerMeter: vResolution)
+ {
+ RedMask = Rgba32RedMask,
+ GreenMask = Rgba32GreenMask,
+ BlueMask = Rgba32BlueMask,
+ Compression = BmpCompression.BitFields
+ };
+
+ if (this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
+ {
+ infoHeader.AlphaMask = Rgba32AlphaMask;
+ }
var fileHeader = new BmpFileHeader(
- type: 19778, // BM
- fileSize: 54 + infoHeader.ImageSize,
+ type: BmpConstants.TypeMarkers.Bitmap,
+ fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize,
reserved: 0,
- offset: 54);
+ offset: BmpFileHeader.Size + infoHeaderSize);
#if NETCOREAPP2_1
- Span buffer = stackalloc byte[40];
+ Span buffer = stackalloc byte[infoHeaderSize];
#else
- byte[] buffer = new byte[40];
+ byte[] buffer = new byte[infoHeaderSize];
#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, BmpFileHeader.Size);
- infoHeader.WriteTo(buffer);
+ infoHeader.WriteV4Header(buffer);
- stream.Write(buffer, 0, 40);
+ stream.Write(buffer, 0, infoHeaderSize);
this.WriteImage(stream, image.Frames.RootFrame);
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
index 4dd63a9626..6da5f73e3f 100644
--- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
@@ -17,19 +17,44 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal struct BmpInfoHeader
{
///
- /// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file.
+ /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
///
- public const int Size = 40;
+ public const int CoreSize = 12;
///
- /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
+ /// Defines the size of the short variant of the OS22XBITMAPHEADER data structure in the bitmap file.
///
- public const int CoreSize = 12;
+ public const int Os22ShortSize = 16;
+
+ ///
+ /// Defines the size of the BITMAPINFOHEADER (BMP Version 3) data structure in the bitmap file.
+ ///
+ public const int SizeV3 = 40;
+
+ ///
+ /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
+ ///
+ public const int AdobeV3Size = 52;
+
+ ///
+ /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks (including the alpha channel) are part of the info header instead of following it.
+ ///
+ public const int AdobeV3WithAlphaSize = 56;
+
+ ///
+ /// Size of a IBM OS/2 2.x bitmap header.
+ ///
+ public const int Os2v2Size = 64;
+
+ ///
+ /// Defines the size of the BITMAPINFOHEADER (BMP Version 4) data structure in the bitmap file.
+ ///
+ public const int SizeV4 = 108;
///
/// Defines the size of the biggest supported header data structure in the bitmap file.
///
- public const int MaxHeaderSize = Size;
+ public const int MaxHeaderSize = SizeV4;
///
/// Defines the size of the field.
@@ -47,7 +72,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int xPelsPerMeter = 0,
int yPelsPerMeter = 0,
int clrUsed = 0,
- int clrImportant = 0)
+ int clrImportant = 0,
+ int redMask = 0,
+ int greenMask = 0,
+ int blueMask = 0,
+ int alphaMask = 0,
+ int csType = 0,
+ int redX = 0,
+ int redY = 0,
+ int redZ = 0,
+ int greenX = 0,
+ int greenY = 0,
+ int greenZ = 0,
+ int blueX = 0,
+ int blueY = 0,
+ int blueZ = 0,
+ int gammeRed = 0,
+ int gammeGreen = 0,
+ int gammeBlue = 0)
{
this.HeaderSize = headerSize;
this.Width = width;
@@ -60,10 +102,27 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.YPelsPerMeter = yPelsPerMeter;
this.ClrUsed = clrUsed;
this.ClrImportant = clrImportant;
+ this.RedMask = redMask;
+ this.GreenMask = greenMask;
+ this.BlueMask = blueMask;
+ this.AlphaMask = alphaMask;
+ this.CsType = csType;
+ this.RedX = redX;
+ this.RedY = redY;
+ this.RedZ = redZ;
+ this.GreenX = greenX;
+ this.GreenY = greenY;
+ this.GreenZ = greenZ;
+ this.BlueX = blueX;
+ this.BlueY = blueY;
+ this.BlueZ = blueZ;
+ this.GammaRed = gammeRed;
+ this.GammaGreen = gammeGreen;
+ this.GammaBlue = gammeBlue;
}
///
- /// Gets or sets the size of this header
+ /// Gets or sets the size of this header.
///
public int HeaderSize { get; set; }
@@ -125,25 +184,94 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public int ClrImportant { get; set; }
///
- /// Parses the full BITMAPINFOHEADER header (40 bytes).
+ /// Gets or sets red color mask. This is used with the BITFIELDS decoding.
///
- /// The data to parse.
- /// Parsed header
- ///
- public static BmpInfoHeader Parse(ReadOnlySpan data)
- {
- if (data.Length != Size)
- {
- throw new ArgumentException(nameof(data), $"Must be {Size} bytes. Was {data.Length} bytes.");
- }
+ public int RedMask { get; set; }
- return MemoryMarshal.Cast(data)[0];
- }
+ ///
+ /// Gets or sets green color mask. This is used with the BITFIELDS decoding.
+ ///
+ public int GreenMask { get; set; }
+
+ ///
+ /// Gets or sets blue color mask. This is used with the BITFIELDS decoding.
+ ///
+ public int BlueMask { get; set; }
///
- /// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
+ /// Gets or sets alpha color mask. This is not used yet.
///
- /// The data to parse,
+ public int AlphaMask { get; set; }
+
+ ///
+ /// Gets or sets the Color space type. Not used yet.
+ ///
+ public int CsType { get; set; }
+
+ ///
+ /// Gets or sets the X coordinate of red endpoint. Not used yet.
+ ///
+ public int RedX { get; set; }
+
+ ///
+ /// Gets or sets the Y coordinate of red endpoint. Not used yet.
+ ///
+ public int RedY { get; set; }
+
+ ///
+ /// Gets or sets the Z coordinate of red endpoint. Not used yet.
+ ///
+ public int RedZ { get; set; }
+
+ ///
+ /// Gets or sets the X coordinate of green endpoint. Not used yet.
+ ///
+ public int GreenX { get; set; }
+
+ ///
+ /// Gets or sets the Y coordinate of green endpoint. Not used yet.
+ ///
+ public int GreenY { get; set; }
+
+ ///
+ /// Gets or sets the Z coordinate of green endpoint. Not used yet.
+ ///
+ public int GreenZ { get; set; }
+
+ ///
+ /// Gets or sets the X coordinate of blue endpoint. Not used yet.
+ ///
+ public int BlueX { get; set; }
+
+ ///
+ /// Gets or sets the Y coordinate of blue endpoint. Not used yet.
+ ///
+ public int BlueY { get; set; }
+
+ ///
+ /// Gets or sets the Z coordinate of blue endpoint. Not used yet.
+ ///
+ public int BlueZ { get; set; }
+
+ ///
+ /// Gets or sets the Gamma red coordinate scale value. Not used yet.
+ ///
+ public int GammaRed { get; set; }
+
+ ///
+ /// Gets or sets the Gamma green coordinate scale value. Not used yet.
+ ///
+ public int GammaGreen { get; set; }
+
+ ///
+ /// Gets or sets the Gamma blue coordinate scale value. Not used yet.
+ ///
+ public int GammaBlue { get; set; }
+
+ ///
+ /// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
+ ///
+ /// The data to parse.
/// Parsed header
///
public static BmpInfoHeader ParseCore(ReadOnlySpan data)
@@ -156,7 +284,161 @@ namespace SixLabors.ImageSharp.Formats.Bmp
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2)));
}
- public unsafe void WriteTo(Span buffer)
+ ///
+ /// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height
+ /// are 4 bytes instead of 2, resulting in 16 bytes total.
+ ///
+ /// The data to parse.
+ /// Parsed header
+ ///
+ public static BmpInfoHeader ParseOs22Short(ReadOnlySpan data)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
+ }
+
+ ///
+ /// Parses the full BMP Version 3 BITMAPINFOHEADER header (40 bytes).
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseV3(ReadOnlySpan data)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
+ compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
+ imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
+ xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
+ yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
+ clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
+ clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)));
+ }
+
+ ///
+ /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
+ /// 52 bytes without the alpha mask, 56 bytes with the alpha mask.
+ ///
+ /// The data to parse.
+ /// Indicates, if the alpha bitmask is present.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan data, bool withAlpha = true)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
+ compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
+ imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
+ xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
+ yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
+ clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
+ clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)),
+ redMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)),
+ greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)),
+ blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)),
+ alphaMask: withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0);
+ }
+
+ ///
+ /// Parses a OS/2 version 2 bitmap header (64 bytes). Only the first 40 bytes are parsed which are
+ /// very similar to the Bitmap v3 header. The other 24 bytes are ignored, but they do not hold any
+ /// useful information for decoding the image.
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan data)
+ {
+ var infoHeader = new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
+
+ int compression = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4));
+
+ // The compression value in OS/2 bitmap has a different meaning than in windows bitmaps.
+ // Map the OS/2 value to the windows values.
+ switch (compression)
+ {
+ case 0:
+ infoHeader.Compression = BmpCompression.RGB;
+ break;
+ case 1:
+ infoHeader.Compression = BmpCompression.RLE8;
+ break;
+ case 2:
+ infoHeader.Compression = BmpCompression.RLE4;
+ break;
+ default:
+ BmpThrowHelper.ThrowImageFormatException($"Compression type is not supported. ImageSharp only supports uncompressed, RLE4 and RLE8.");
+ break;
+ }
+
+ infoHeader.ImageSize = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4));
+ infoHeader.XPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4));
+ infoHeader.YPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4));
+ infoHeader.ClrUsed = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4));
+ infoHeader.ClrImportant = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4));
+
+ // The following 24 bytes of the header are omitted.
+ return infoHeader;
+ }
+
+ ///
+ /// Parses the full BMP Version 4 BITMAPINFOHEADER header (108 bytes).
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseV4(ReadOnlySpan data)
+ {
+ if (data.Length != SizeV4)
+ {
+ throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes.");
+ }
+
+ return MemoryMarshal.Cast(data)[0];
+ }
+
+ ///
+ /// Writes a bitmap version 3 (Microsoft Windows NT) header to a buffer (40 bytes).
+ ///
+ /// The buffer to write to.
+ public void WriteV3Header(Span buffer)
+ {
+ buffer.Clear();
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(0, 4), SizeV3);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(28, 4), this.YPelsPerMeter);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(32, 4), this.ClrUsed);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(36, 4), this.ClrImportant);
+ }
+
+ ///
+ /// Writes a complete Bitmap V4 header to a buffer.
+ ///
+ /// The buffer to write to.
+ public unsafe void WriteV4Header(Span buffer)
{
ref BmpInfoHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer));
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
new file mode 100644
index 0000000000..a92a19d9ba
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Bmp
+{
+ ///
+ /// Enum value for the different bitmap info header types. The enum value is the number of bytes for the specific bitmap header.
+ ///
+ public enum BmpInfoHeaderType
+ {
+ ///
+ /// Bitmap Core or BMP Version 2 header (Microsoft Windows 2.x).
+ ///
+ WinVersion2 = 12,
+
+ ///
+ /// Short variant of the OS/2 Version 2 bitmap header.
+ ///
+ Os2Version2Short = 16,
+
+ ///
+ /// BMP Version 3 header (Microsoft Windows 3.x or Microsoft Windows NT).
+ ///
+ WinVersion3 = 40,
+
+ ///
+ /// Adobe variant of the BMP Version 3 header.
+ ///
+ AdobeVersion3 = 52,
+
+ ///
+ /// Adobe variant of the BMP Version 3 header with an alpha mask.
+ ///
+ AdobeVersion3WithAlpha = 56,
+
+ ///
+ /// BMP Version 2.x header (IBM OS/2 2.x).
+ ///
+ Os2Version2 = 64,
+
+ ///
+ /// BMP Version 4 header (Microsoft Windows 95).
+ ///
+ WinVersion4 = 108,
+
+ ///
+ /// BMP Version 5 header (Windows NT 5.0, 98 or later).
+ ///
+ WinVersion5 = 124,
+ }
+}
diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs
index 8b33e30fa6..2d86856177 100644
--- a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs
@@ -19,7 +19,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private BmpMetaData(BmpMetaData other) => this.BitsPerPixel = other.BitsPerPixel;
+ private BmpMetaData(BmpMetaData other)
+ {
+ this.BitsPerPixel = other.BitsPerPixel;
+ this.InfoHeaderType = other.InfoHeaderType;
+ }
+
+ ///
+ /// Gets or sets the bitmap info header type.
+ ///
+ public BmpInfoHeaderType InfoHeaderType { get; set; }
///
/// Gets or sets the number of bits per pixel.
diff --git a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
new file mode 100644
index 0000000000..443471404e
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Formats.Bmp
+{
+ internal static class BmpThrowHelper
+ {
+ ///
+ /// Cold path optimization for throwing -s
+ ///
+ /// The error message for the exception.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowImageFormatException(string errorMessage)
+ {
+ throw new ImageFormatException(errorMessage);
+ }
+
+ ///
+ /// Cold path optimization for throwing -s
+ ///
+ /// The error message for the exception.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowNotSupportedException(string errorMessage)
+ {
+ throw new NotSupportedException(errorMessage);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
index 2fdc233b0c..c8bd286746 100644
--- a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
namespace SixLabors.ImageSharp.Formats.Gif
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index 09ed6408d7..1f47de594c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -9,10 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
- private static readonly Vector4 CMin4 = new Vector4(0F);
- private static readonly Vector4 CMax4 = new Vector4(255F);
- private static readonly Vector4 COff4 = new Vector4(128F);
-
///
/// Transpose the block into the destination block.
///
@@ -94,10 +91,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0, 255]
+ /// Level shift by +maximum/2, clip to [0, maximum]
///
- public void NormalizeColorsInplace()
+ public void NormalizeColorsInplace(float maximum)
{
+ Vector4 CMin4 = new Vector4(0F);
+ Vector4 CMax4 = new Vector4(maximum);
+ Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2));
+
this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4);
this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4);
this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4);
@@ -120,10 +121,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing and in one step.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void NormalizeColorsAndRoundInplaceAvx2()
+ public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
- Vector off = new Vector(128f);
- Vector max = new Vector(255F);
+ Vector off = new Vector((float)Math.Ceiling(maximum/2));
+ Vector max = new Vector(maximum);
ref Vector row0 = ref Unsafe.As>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
index f93ee6522d..ec4e06e429 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
@@ -11,6 +11,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -22,10 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
- private static readonly Vector4 CMin4 = new Vector4(0F);
- private static readonly Vector4 CMax4 = new Vector4(255F);
- private static readonly Vector4 COff4 = new Vector4(128F);
-
///
/// Transpose the block into the destination block.
///
@@ -59,10 +56,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0, 255]
+ /// Level shift by +maximum/2, clip to [0, maximum]
///
- public void NormalizeColorsInplace()
+ public void NormalizeColorsInplace(float maximum)
{
+ Vector4 CMin4 = new Vector4(0F);
+ Vector4 CMax4 = new Vector4(maximum);
+ Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2));
+
<#
PushIndent(" ");
@@ -83,10 +84,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing and in one step.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void NormalizeColorsAndRoundInplaceAvx2()
+ public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
- Vector off = new Vector(128f);
- Vector max = new Vector(255F);
+ Vector off = new Vector((float)Math.Ceiling(maximum/2));
+ Vector max = new Vector(maximum);
<#
for (int i = 0; i < 8; i++)
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 81393342d6..c9c886f057 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -467,17 +467,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0..255], and round all the values in the block.
+ /// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
///
- public void NormalizeColorsAndRoundInplace()
+ public void NormalizeColorsAndRoundInplace(float maximum)
{
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
- this.NormalizeColorsAndRoundInplaceAvx2();
+ this.NormalizeColorsAndRoundInplaceAvx2(maximum);
}
else
{
- this.NormalizeColorsInplace();
+ this.NormalizeColorsInplace(maximum);
this.RoundInplace();
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
index af0938d302..43de39ac78 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
@@ -100,13 +100,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
public override int GetHashCode()
{
- return HashHelpers.Combine(
- this.DCTEncodeVersion.GetHashCode(),
- HashHelpers.Combine(
- this.APP14Flags0.GetHashCode(),
- HashHelpers.Combine(
- this.APP14Flags1.GetHashCode(),
- this.ColorTransform.GetHashCode())));
+ return HashCode.Combine(
+ this.DCTEncodeVersion,
+ this.APP14Flags0,
+ this.APP14Flags1,
+ this.ColorTransform);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
index 7a14d072e6..d4dc31fe0c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
@@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromCmyk : JpegColorConverter
{
- public FromCmyk()
- : base(JpegColorSpace.Cmyk)
+ public FromCmyk(int precision)
+ : base(JpegColorSpace.Cmyk, precision)
{
}
@@ -25,14 +25,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
for (int i = 0; i < result.Length; i++)
{
float c = cVals[i];
float m = mVals[i];
float y = yVals[i];
- float k = kVals[i] / 255F;
+ float k = kVals[i] / this.MaximumValue;
v.X = c * k;
v.Y = m * k;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
index 7424145c3b..4a5dfa6322 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
@@ -12,14 +12,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromGrayscale : JpegColorConverter
{
- public FromGrayscale()
- : base(JpegColorSpace.Grayscale)
+ public FromGrayscale(int precision)
+ : base(JpegColorSpace.Grayscale, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span result)
{
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
index 7cd97c4140..516dfb39fe 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
@@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromRgb : JpegColorConverter
{
- public FromRgb()
- : base(JpegColorSpace.RGB)
+ public FromRgb(int precision)
+ : base(JpegColorSpace.RGB, precision)
{
}
@@ -24,7 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
for (int i = 0; i < result.Length; i++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
index cb71889bc5..124aac1224 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
@@ -10,17 +10,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYCbCrBasic : JpegColorConverter
{
- public FromYCbCrBasic()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrBasic(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span result)
{
- ConvertCore(values, result);
+ ConvertCore(values, result, this.MaximumValue, this.HalfValue);
}
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
// TODO: We can optimize a lot here with Vector and SRCS.Unsafe()!
ReadOnlySpan yVals = values.Component0;
@@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(1 / maxValue, 1 / maxValue, 1 / maxValue, 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
- float cb = cbVals[i] - 128F;
- float cr = crVals[i] - 128F;
+ float cb = cbVals[i] - halfValue;
+ float cr = crVals[i] - halfValue;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
index 23aa1acbe6..10ef02a931 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
@@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYCbCrSimd : JpegColorConverter
{
- public FromYCbCrSimd()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrSimd(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
@@ -25,16 +25,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
- ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
+ ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
}
- FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
+ FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
}
///
/// SIMD convert using buffers of sizes divisible by 8.
///
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
@@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase =
ref Unsafe.As(ref MemoryMarshal.GetReference(result));
- var chromaOffset = new Vector4(-128f);
+ var chromaOffset = new Vector4(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// y = yVals[i];
Vector4Pair y = Unsafe.Add(ref yBase, i);
- // cb = cbVals[i] - 128F;
+ // cb = cbVals[i] - halfValue);
Vector4Pair cb = Unsafe.Add(ref cbBase, i);
cb.AddInplace(chromaOffset);
- // cr = crVals[i] - 128F;
+ // cr = crVals[i] - halfValue;
Vector4Pair cr = Unsafe.Add(ref crBase, i);
cr.AddInplace(chromaOffset);
@@ -90,15 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
if (Vector.Count == 4)
{
// TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector is terrible?)
- r.RoundAndDownscalePreAvx2();
- g.RoundAndDownscalePreAvx2();
- b.RoundAndDownscalePreAvx2();
+ r.RoundAndDownscalePreAvx2(maxValue);
+ g.RoundAndDownscalePreAvx2(maxValue);
+ b.RoundAndDownscalePreAvx2(maxValue);
}
else if (SimdUtils.IsAvx2CompatibleArchitecture)
{
- r.RoundAndDownscaleAvx2();
- g.RoundAndDownscaleAvx2();
- b.RoundAndDownscaleAvx2();
+ r.RoundAndDownscaleAvx2(maxValue);
+ g.RoundAndDownscaleAvx2(maxValue);
+ b.RoundAndDownscaleAvx2(maxValue);
}
else
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
index f0a70a6f38..9953f78c18 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
@@ -15,8 +15,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYCbCrSimdAvx2 : JpegColorConverter
{
- public FromYCbCrSimdAvx2()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrSimdAvx2(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
@@ -28,16 +28,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
- ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
+ ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
}
- FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
+ FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
}
///
/// SIMD convert using buffers of sizes divisible by 8.
///
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
// This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s.
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase =
ref Unsafe.As(ref MemoryMarshal.GetReference(result));
- var chromaOffset = new Vector(-128f);
+ var chromaOffset = new Vector(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector ggRefAsVector = ref Unsafe.As>(ref gg);
ref Vector bbRefAsVector = ref Unsafe.As>(ref bb);
- var scale = new Vector(1 / 255f);
+ var scale = new Vector(1 / maxValue);
for (int i = 0; i < n; i++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
index 6f940f62f9..94be11e237 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
@@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYccK : JpegColorConverter
{
- public FromYccK()
- : base(JpegColorSpace.Ycck)
+ public FromYccK(int precision)
+ : base(JpegColorSpace.Ycck, precision)
{
}
@@ -25,18 +25,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
- float cb = cbVals[i] - 128F;
- float cr = crVals[i] - 128F;
- float k = kVals[i] / 255F;
+ float cb = cbVals[i] - this.HalfValue;
+ float cr = crVals[i] - this.HalfValue;
+ float k = kVals[i] / this.MaximumValue;
- v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
- v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
- v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
+ v.X = (this.MaximumValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
+ v.Y = (this.MaximumValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
+ v.Z = (this.MaximumValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
v *= scale;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
index a44ebf89d1..c2e390c590 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
@@ -3,12 +3,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
-using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@@ -22,15 +20,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
private static readonly JpegColorConverter[] Converters =
{
- GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayscale(), new FromRgb()
+ // 8-bit converters
+ GetYCbCrConverter(8),
+ new FromYccK(8),
+ new FromCmyk(8),
+ new FromGrayscale(8),
+ new FromRgb(8),
+
+ // 12-bit converters
+ GetYCbCrConverter(12),
+ new FromYccK(12),
+ new FromCmyk(12),
+ new FromGrayscale(12),
+ new FromRgb(12),
};
///
/// Initializes a new instance of the class.
///
- protected JpegColorConverter(JpegColorSpace colorSpace)
+ protected JpegColorConverter(JpegColorSpace colorSpace, int precision)
{
this.ColorSpace = colorSpace;
+ this.Precision = precision;
+ this.MaximumValue = (float)Math.Pow(2, precision) - 1;
+ this.HalfValue = (float)Math.Ceiling(this.MaximumValue / 2);
}
///
@@ -38,12 +51,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
public JpegColorSpace ColorSpace { get; }
+ ///
+ /// Gets the Precision of this converter in bits.
+ ///
+ public int Precision { get; }
+
+ ///
+ /// Gets the maximum value of a sample
+ ///
+ private float MaximumValue { get; }
+
+ ///
+ /// Gets the half of the maximum value of a sample
+ ///
+ private float HalfValue { get; }
+
///
/// Returns the corresponding to the given
///
- public static JpegColorConverter GetConverter(JpegColorSpace colorSpace)
+ public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, float precision)
{
- JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace);
+ JpegColorConverter converter = Array.Find(Converters, c => c.ColorSpace == colorSpace
+ && c.Precision == precision);
if (converter is null)
{
@@ -63,8 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
/// Returns the for the YCbCr colorspace that matches the current CPU architecture.
///
- private static JpegColorConverter GetYCbCrConverter() =>
- FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
+ private static JpegColorConverter GetYCbCrConverter(int precision) =>
+ FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2(precision) : new FromYCbCrSimd(precision);
///
/// A stack-only struct to reference the input buffers using -s.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
index 9e134746bc..9e11981b12 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
@@ -50,8 +50,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// The huffman values
public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values)
{
- const int Length = 257;
- using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length))
+ // We do some bounds checks in the code here to protect against AccessViolationExceptions
+ const int HuffCodeLength = 257;
+ const int MaxSizeLength = HuffCodeLength - 1;
+ using (IMemoryOwner huffcode = memoryAllocator.Allocate(HuffCodeLength))
{
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
@@ -63,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (short i = 1; i < 17; i++)
{
byte length = Unsafe.Add(ref codeLengthsRef, i);
- for (short j = 0; j < length; j++)
+ for (short j = 0; j < length && x < MaxSizeLength; j++)
{
Unsafe.Add(ref sizesRef, x++) = i;
}
@@ -84,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Unsafe.Add(ref valOffsetRef, k) = (int)(si - code);
if (Unsafe.Add(ref sizesRef, si) == k)
{
- while (Unsafe.Add(ref sizesRef, si) == k)
+ while (Unsafe.Add(ref sizesRef, si) == k && si < HuffCodeLength)
{
Unsafe.Add(ref huffcodeRef, si++) = (short)code++;
}
@@ -100,19 +102,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Generate non-spec lookup tables to speed up decoding.
const int FastBits = ScanDecoder.FastBits;
- ref byte fastRef = ref this.Lookahead[0];
- Unsafe.InitBlockUnaligned(ref fastRef, 0xFF, 1 << FastBits); // Flag for non-accelerated
+ ref byte lookaheadRef = ref this.Lookahead[0];
+ const uint MaxFastLength = 1 << FastBits;
+ Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, MaxFastLength); // Flag for non-accelerated
for (int i = 0; i < si; i++)
{
int size = Unsafe.Add(ref sizesRef, i);
if (size <= FastBits)
{
- int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size);
- int m = 1 << (FastBits - size);
- for (int l = 0; l < m; l++)
+ int huffCode = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size);
+ int max = 1 << (FastBits - size);
+ for (int left = 0; left < max; left++)
{
- Unsafe.Add(ref fastRef, c + l) = (byte)i;
+ Unsafe.Add(ref lookaheadRef, huffCode + left) = (byte)i;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
index 1454bb5b12..ace8d7215b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
@@ -29,10 +29,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
JpegColorSpace ColorSpace { get; }
+ ///
+ /// Gets the number of bits used for precision.
+ ///
+ int Precision { get; }
+
///
/// Gets the components.
///
- IEnumerable Components { get; }
+ IJpegComponent[] Components { get; }
///
/// Gets the quantization tables, in zigzag order.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
index 4bff492486..c51a2f4da5 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
@@ -112,13 +112,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
public override int GetHashCode()
{
- return HashHelpers.Combine(
- this.MajorVersion.GetHashCode(),
- HashHelpers.Combine(
- this.MinorVersion.GetHashCode(),
- HashHelpers.Combine(
- this.DensityUnits.GetHashCode(),
- HashHelpers.Combine(this.XDensity, this.YDensity))));
+ return HashCode.Combine(
+ this.MajorVersion,
+ this.MinorVersion,
+ this.DensityUnits,
+ this.XDensity,
+ this.YDensity);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
index da4b2847b8..fe39f41884 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
@@ -38,6 +39,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
private Size subSamplingDivisors;
+ ///
+ /// Defines the maximum value derived from the bitdepth
+ ///
+ private int maximumValue;
+
///
/// Initializes a new instance of the struct.
///
@@ -48,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int qtIndex = component.QuantizationTableIndex;
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
this.subSamplingDivisors = component.SubSamplingDivisors;
+ this.maximumValue = (int)Math.Pow(2, decoder.Precision) - 1;
this.SourceBlock = default;
this.WorkspaceBlock1 = default;
@@ -58,14 +65,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Processes 'sourceBlock' producing Jpeg color channel values from spectral values:
/// - Dequantize
/// - Applying IDCT
- /// - Level shift by +128, clip to [0, 255]
+ /// - Level shift by +maximumValue/2, clip to [0, maximumValue]
/// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in
///
/// The source block.
/// The destination buffer area.
+ /// The maximum value derived from the bitdepth.
public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock,
- in BufferArea destArea)
+ in BufferArea destArea,
+ float maximumValue)
{
ref Block8x8F b = ref this.SourceBlock;
b.LoadFrom(ref sourceBlock);
@@ -78,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations.
// To be "more accurate", we need to emulate this by rounding!
- this.WorkspaceBlock1.NormalizeColorsAndRoundInplace();
+ this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue);
this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height);
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
index 94ec600dd5..e7f3e4fda5 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
@@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public void CopyBlocksToColorBuffer()
{
var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component);
+ float maximumValue = (float)Math.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1;
for (int y = 0; y < this.BlockRowsPerStep; y++)
{
@@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.blockAreaSize.Width,
this.blockAreaSize.Height);
- blockPp.ProcessBlockColorsInto(ref block, destArea);
+ blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
index 7ce86b4c9b..438749abf9 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers;
-using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@@ -57,14 +56,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
this.configuration = configuration;
this.RawJpeg = rawJpeg;
- IJpegComponent c0 = rawJpeg.Components.First();
+ IJpegComponent c0 = rawJpeg.Components[0];
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
- this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
+
+ this.ComponentProcessors = new JpegComponentPostProcessor[rawJpeg.Components.Length];
+ for (int i = 0; i < rawJpeg.Components.Length; i++)
+ {
+ this.ComponentProcessors[i] = new JpegComponentPostProcessor(memoryAllocator, this, rawJpeg.Components[i]);
+ }
+
this.rgbaBuffer = memoryAllocator.Allocate(rawJpeg.ImageSizeInPixels.Width);
- this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
+ this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision);
}
///
@@ -152,7 +157,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
- Buffer2D[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray();
+ var buffers = new Buffer2D[this.ComponentProcessors.Length];
+ for (int i = 0; i < this.ComponentProcessors.Length; i++)
+ {
+ buffers[i] = this.ComponentProcessors[i].ColorBuffer;
+ }
for (int yy = this.PixelRowCounter; yy < maxY; yy++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
index 48abca2a4e..a1c1b023e4 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
@@ -474,7 +474,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Unsafe.Add(ref blockDataRef, zig) = (short)this.ExtendReceive(s);
}
}
- } while (k < 64);
+ }
+ while (k < 64);
}
private void DecodeBlockProgressiveDC(
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
index d775425c5c..301079b6ae 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index f6da9cb2ec..06c844d58e 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers.Binary;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -33,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The only supported precision
///
- public const int SupportedPrecision = 8;
+ private readonly int[] supportedPrecisions = { 8, 12 };
///
/// The global configuration
@@ -137,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Gets the color depth, in number of bits per pixel.
///
- public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
+ public int BitsPerPixel => this.ComponentCount * this.Frame.Precision;
///
/// Gets the input stream.
@@ -160,13 +159,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
public JpegColorSpace ColorSpace { get; private set; }
+ ///
+ public int Precision { get; private set; }
+
///
/// Gets the components.
///
public JpegComponent[] Components => this.Frame.Components;
///
- IEnumerable IRawJpegData.Components => this.Components;
+ IJpegComponent[] IRawJpegData.Components => this.Components;
///
public Block8x8F[] QuantizationTables { get; private set; }
@@ -553,12 +555,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (this.exifData is null)
{
// The first 6 bytes (Exif00) will be skipped, because this is Jpeg specific
- this.exifData = profile.Skip(Exif00).ToArray();
+ this.exifData = profile.AsSpan(Exif00).ToArray();
}
else
{
// If the EXIF information exceeds 64K, it will be split over multiple APP1 markers
- this.ExtendProfile(ref this.exifData, profile.Skip(Exif00).ToArray());
+ this.ExtendProfile(ref this.exifData, profile.AsSpan(Exif00).ToArray());
}
}
}
@@ -720,12 +722,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, remaining);
- // We only support 8-bit precision.
- if (this.temp[0] != SupportedPrecision)
+ // We only support 8-bit and 12-bit precision.
+ if (!this.supportedPrecisions.Contains(this.temp[0]))
{
- throw new ImageFormatException("Only 8-Bit precision supported.");
+ throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported.");
}
+ this.Precision = this.temp[0];
+
this.Frame = new JpegFrame
{
Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,
@@ -855,6 +859,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
private void ProcessStartOfScanMarker()
{
+ if (this.Frame is null)
+ {
+ throw new ImageFormatException("No readable SOFn (Start Of Frame) marker found.");
+ }
+
int selectorsCount = this.InputStream.ReadByte();
for (int i = 0; i < selectorsCount; i++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 0dcbd8fef7..b3f30f815f 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -547,7 +547,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteDefineHuffmanTables(int componentCount)
{
// Table identifiers.
- Span headers = stackalloc byte[] { 0x00, 0x10, 0x01, 0x11 };
+ Span headers = stackalloc byte[]
+ {
+ 0x00,
+ 0x10,
+ 0x01,
+ 0x11
+ };
int markerlen = 2;
HuffmanSpec[] specs = HuffmanSpec.TheHuffmanSpecs;
@@ -786,16 +792,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteStartOfFrame(int width, int height, int componentCount)
{
// "default" to 4:2:0
- Span subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
- Span chroma = stackalloc byte[] { 0x00, 0x01, 0x01 };
+ Span subsamples = stackalloc byte[]
+ {
+ 0x22,
+ 0x11,
+ 0x11
+ };
+
+ Span chroma = stackalloc byte[]
+ {
+ 0x00,
+ 0x01,
+ 0x01
+ };
switch (this.subsample)
{
case JpegSubsample.Ratio444:
- subsamples = stackalloc byte[] { 0x11, 0x11, 0x11 };
+ subsamples = stackalloc byte[]
+ {
+ 0x11,
+ 0x11,
+ 0x11
+ };
break;
case JpegSubsample.Ratio420:
- subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
+ subsamples = stackalloc byte[]
+ {
+ 0x22,
+ 0x11,
+ 0x11
+ };
break;
}
diff --git a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
index 6ab0dd6576..1d0e280bd3 100644
--- a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Buffers.Binary;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.MetaData;
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index c6a297e33a..5d9dc6a890 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -57,7 +57,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Up(x) = Raw(x) - Prior(x)
resultBaseRef = 2;
- for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) {
+ for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */)
+ {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
++x;
diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs
index 62a7b74aba..e1f978e1ac 100644
--- a/src/ImageSharp/Formats/Png/PngConstants.cs
+++ b/src/ImageSharp/Formats/Png/PngConstants.cs
@@ -26,7 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Png
///
public static readonly IEnumerable FileExtensions = new[] { "png" };
- public static readonly byte[] HeaderBytes = {
+ public static readonly byte[] HeaderBytes =
+ {
0x89, // Set the high bit.
0x50, // P
0x4E, // N
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 292de007ca..216f3129f9 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
// Use the metadata to determine what quantization depth to use if no quantizer has been set.
- if (this.quantizer == null)
+ if (this.quantizer is null)
{
this.quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits));
}
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index c19c92426f..6ab609d5c0 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -1,10 +1,15 @@
- A cross-platform library for the processing of image files; written in C#
SixLabors.ImageSharp
+ Six Labors and contributors
+ Six Labors
+ Copyright (c) Six Labors and contributors.
+ SixLabors.ImageSharp
+ A cross-platform library for the processing of image files; written in C#
+ en
+
$(packageversion)
0.0.1
- Six Labors and contributors
netstandard1.3;netstandard2.0;netcoreapp2.1;net472
true
true
@@ -16,39 +21,27 @@
http://www.apache.org/licenses/LICENSE-2.0
git
https://github.com/SixLabors/ImageSharp
- false
- false
- false
- false
- false
- false
- false
- false
- false
full
portable
True
IOperation
- Latest
+ 7.3
$(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
-
+
-
+
+
+
+
-
-
-
- All
-
-
@@ -56,14 +49,16 @@
-
+
- ..\..\ImageSharp.ruleset
+ ..\..\standards\SixLabors.ruleset
SixLabors.ImageSharp
+
true
+
TextTemplatingFileGenerator
@@ -126,6 +121,7 @@
TextTemplatingFileGenerator
+
True
@@ -203,6 +199,7 @@
PorterDuffFunctions.Generated.tt
+
diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs
index 0750e0368c..501ed5e69c 100644
--- a/src/ImageSharp/Memory/RowInterval.cs
+++ b/src/ImageSharp/Memory/RowInterval.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs
index a08a95fed4..24a3686de2 100644
--- a/src/ImageSharp/MetaData/ImageProperty.cs
+++ b/src/ImageSharp/MetaData/ImageProperty.cs
@@ -99,19 +99,7 @@ namespace SixLabors.ImageSharp.MetaData
///
/// A 32-bit signed integer that is the hash code for this instance.
///
- public override int GetHashCode()
- {
- unchecked
- {
- int hashCode = this.Name.GetHashCode();
- if (this.Value != null)
- {
- hashCode = (hashCode * 397) ^ this.Value.GetHashCode();
- }
-
- return hashCode;
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Name, this.Value);
///
/// Returns the fully qualified type name of this instance.
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs
index 555cadafee..fb2a893d1c 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs
@@ -5,14 +5,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
internal static class ExifConstants
{
- public static readonly byte[] LittleEndianByteOrderMarker = {
+ public static readonly byte[] LittleEndianByteOrderMarker =
+ {
(byte)'I',
(byte)'I',
0x2A,
0x00,
};
- public static readonly byte[] BigEndianByteOrderMarker = {
+ public static readonly byte[] BigEndianByteOrderMarker =
+ {
(byte)'M',
(byte)'M',
0x00,
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
index 100649c0fd..8ec9eea275 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
@@ -27,9 +27,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
public ExifReader(byte[] exifData)
{
- DebugGuard.NotNull(exifData, nameof(exifData));
-
- this.exifData = exifData;
+ this.exifData = exifData ?? throw new ArgumentNullException(nameof(exifData));
}
private delegate TDataType ConverterMethod(ReadOnlySpan data);
@@ -374,7 +372,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private void AddInvalidTag(ExifTag tag)
{
- if (this.invalidTags == null)
+ if (this.invalidTags is null)
{
this.invalidTags = new List();
}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
index e497fc7fa4..e999d00b86 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
@@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
LensMake,
LensModel,
LensSerialNumber
- };
+ };
///
/// The collection of GPS tags
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
index ccacfd0bf9..409c55253a 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
@@ -198,7 +198,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
///
- public override int GetHashCode() => this.GetHashCode(this);
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(this.Tag, this.DataType, this.Value);
+ }
///
public override string ToString()
@@ -714,20 +717,5 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
throw new NotSupportedException();
}
}
-
- ///
- /// 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(ExifValue exif)
- {
- int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode();
- return hashCode ^ exif.Value?.GetHashCode() ?? hashCode;
- }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
index a241acd216..efc2ca5377 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
@@ -154,18 +154,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = (int)this.Type;
- hashCode = (hashCode * 397) ^ this.G.GetHashCode();
- hashCode = (hashCode * 397) ^ this.A.GetHashCode();
- hashCode = (hashCode * 397) ^ this.B.GetHashCode();
- hashCode = (hashCode * 397) ^ this.C.GetHashCode();
- hashCode = (hashCode * 397) ^ this.D.GetHashCode();
- hashCode = (hashCode * 397) ^ this.E.GetHashCode();
- hashCode = (hashCode * 397) ^ this.F.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Type,
+ this.G.GetHashCode(),
+ this.A.GetHashCode(),
+ this.B.GetHashCode(),
+ this.C.GetHashCode(),
+ this.D.GetHashCode(),
+ this.E.GetHashCode(),
+ this.F.GetHashCode());
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
index 02ab301bd2..de08485ac8 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
@@ -73,13 +73,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = (int)this.CurveType;
- hashCode = (hashCode * 397) ^ (this.XyzValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.ResponseArrays?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.CurveType,
+ this.XyzValues,
+ this.ResponseArrays);
}
private bool EqualsResponseArray(IccResponseCurve other)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs
index 2687e10b66..4c6bb07858 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (int)this.Signature * 397;
- }
- }
+ public override int GetHashCode() => this.Signature.GetHashCode();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs
index a87dae8c5d..e9a812d8ca 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs
@@ -110,13 +110,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.ColorantType;
- hashCode = (hashCode * 397) ^ (this.ChannelValues?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.ColorantType,
+ this.ChannelValues);
}
private static double[][] GetColorantArray(IccColorantEncoding colorantType)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs
index 54c2056151..b5f8fd5c49 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs
@@ -70,10 +70,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.ColorantNumber?.GetHashCode() ?? 0);
- }
+ return HashCode.Combine(this.Signature, this.ColorantNumber);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs
index dd99a2f9fb..9f2b4c90ad 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs
@@ -72,10 +72,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.ColorantData?.GetHashCode() ?? 0);
- }
+ return HashCode.Combine(this.Signature, this.ColorantData);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs
index cc1aea3192..4d393dfb33 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs
@@ -121,16 +121,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.PostScriptProductName?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.RenderingIntent0Crd?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.RenderingIntent1Crd?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.RenderingIntent2Crd?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.RenderingIntent3Crd?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.PostScriptProductName,
+ this.RenderingIntent0Crd,
+ this.RenderingIntent1Crd,
+ this.RenderingIntent2Crd,
+ this.RenderingIntent3Crd);
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs
index 77a913b14e..0d34d3ceba 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs
@@ -116,12 +116,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.CurveData?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.CurveData);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs
index 4510882904..0b8367303d 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs
@@ -91,13 +91,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.Data?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ this.IsAscii.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.Data,
+ this.IsAscii);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs
index 792c653f6c..104d243099 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs
@@ -66,10 +66,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- return (base.GetHashCode() * 397) ^ this.Value.GetHashCode();
- }
+ return HashCode.Combine(this.Signature, this.Value);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs
index a76310927b..0a114ea1f9 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs
index 9a7f2123e2..415f6941ec 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs
@@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
internal sealed class IccLut16TagDataEntry : IccTagDataEntry, IEquatable
{
- private static readonly float[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
+ private static readonly float[,] IdentityMatrix =
+ {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
///
/// Initializes a new instance of the class.
@@ -62,17 +67,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
: base(IccTypeSignature.Lut16, tagSignature)
{
Guard.NotNull(matrix, nameof(matrix));
- Guard.NotNull(inputValues, nameof(inputValues));
- Guard.NotNull(clutValues, nameof(clutValues));
- Guard.NotNull(outputValues, nameof(outputValues));
bool is3By3 = matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3;
Guard.IsTrue(is3By3, nameof(matrix), "Matrix must have a size of three by three");
this.Matrix = this.CreateMatrix(matrix);
- this.InputValues = inputValues;
- this.ClutValues = clutValues;
- this.OutputValues = outputValues;
+ this.InputValues = inputValues ?? throw new ArgumentNullException(nameof(inputValues));
+ this.ClutValues = clutValues ?? throw new ArgumentNullException(nameof(clutValues));
+ this.OutputValues = outputValues ?? throw new ArgumentNullException(nameof(outputValues));
Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size");
Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size");
@@ -143,15 +145,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Matrix.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.InputValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.OutputValues?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.Matrix,
+ this.InputValues,
+ this.ClutValues,
+ this.OutputValues);
}
private Matrix4x4 CreateMatrix(float[,] matrix)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs
index bc0335cd8b..04a49e316d 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs
@@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
internal sealed class IccLut8TagDataEntry : IccTagDataEntry, IEquatable
{
- private static readonly float[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
+ private static readonly float[,] IdentityMatrix =
+ {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
///
/// Initializes a new instance of the class.
@@ -62,17 +67,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
: base(IccTypeSignature.Lut8, tagSignature)
{
Guard.NotNull(matrix, nameof(matrix));
- Guard.NotNull(inputValues, nameof(inputValues));
- Guard.NotNull(clutValues, nameof(clutValues));
- Guard.NotNull(outputValues, nameof(outputValues));
bool is3By3 = matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3;
Guard.IsTrue(is3By3, nameof(matrix), "Matrix must have a size of three by three");
this.Matrix = this.CreateMatrix(matrix);
- this.InputValues = inputValues;
- this.ClutValues = clutValues;
- this.OutputValues = outputValues;
+ this.InputValues = inputValues ?? throw new ArgumentNullException(nameof(inputValues));
+ this.ClutValues = clutValues ?? throw new ArgumentNullException(nameof(clutValues));
+ this.OutputValues = outputValues ?? throw new ArgumentNullException(nameof(outputValues));
Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size");
Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size");
@@ -146,15 +148,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Matrix.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.InputValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.OutputValues?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.Matrix,
+ this.InputValues,
+ this.ClutValues,
+ this.OutputValues);
}
private Matrix4x4 CreateMatrix(float[,] matrix)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs
index 22d5f7b2f1..f7c0946320 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs
@@ -183,19 +183,21 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.InputChannelCount;
- hashCode = (hashCode * 397) ^ this.OutputChannelCount;
- hashCode = (hashCode * 397) ^ this.Matrix3x3.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Matrix3x1.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.CurveB?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.CurveM?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.CurveA?.GetHashCode() ?? 0);
- return hashCode;
- }
+#pragma warning disable SA1129 // Do not use default value type constructor
+ var hashCode = new HashCode();
+#pragma warning restore SA1129 // Do not use default value type constructor
+
+ hashCode.Add(this.Signature);
+ hashCode.Add(this.InputChannelCount);
+ hashCode.Add(this.OutputChannelCount);
+ hashCode.Add(this.Matrix3x3);
+ hashCode.Add(this.Matrix3x1);
+ hashCode.Add(this.ClutValues);
+ hashCode.Add(this.CurveB);
+ hashCode.Add(this.CurveM);
+ hashCode.Add(this.CurveA);
+
+ return hashCode.ToHashCode();
}
private bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs
index a739358b56..27572acd0b 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs
@@ -183,19 +183,21 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.InputChannelCount;
- hashCode = (hashCode * 397) ^ this.OutputChannelCount;
- hashCode = (hashCode * 397) ^ this.Matrix3x3.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Matrix3x1.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.CurveB?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.CurveM?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.CurveA?.GetHashCode() ?? 0);
- return hashCode;
- }
+#pragma warning disable SA1129 // Do not use default value type constructor
+ var hashCode = new HashCode();
+#pragma warning restore SA1129 // Do not use default value type constructor
+
+ hashCode.Add(this.Signature);
+ hashCode.Add(this.InputChannelCount);
+ hashCode.Add(this.OutputChannelCount);
+ hashCode.Add(this.Matrix3x3);
+ hashCode.Add(this.Matrix3x1);
+ hashCode.Add(this.ClutValues);
+ hashCode.Add(this.CurveB);
+ hashCode.Add(this.CurveM);
+ hashCode.Add(this.CurveA);
+
+ return hashCode.ToHashCode();
}
private bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs
index 262129a380..9247ca593f 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs
@@ -106,16 +106,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.Observer;
- hashCode = (hashCode * 397) ^ this.XyzBacking.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.Geometry;
- hashCode = (hashCode * 397) ^ this.Flare.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.Illuminant;
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.Observer,
+ this.XyzBacking,
+ this.Geometry,
+ this.Flare,
+ this.Illuminant);
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs
index 48ed048bf9..1a06eb5880 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs
@@ -66,12 +66,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Texts?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Texts);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs
index 1429a0a878..f13fdb17fb 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs
@@ -90,14 +90,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.InputChannelCount;
- hashCode = (hashCode * 397) ^ this.OutputChannelCount;
- hashCode = (hashCode * 397) ^ (this.Data?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.InputChannelCount,
+ this.OutputChannelCount,
+ this.Data);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs
index c32a45182d..957b8d5c3f 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs
@@ -83,6 +83,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
if (colors.Length > 0)
{
coordinateCount = colors[0].DeviceCoordinates?.Length ?? 0;
+
Guard.IsFalse(colors.Any(t => (t.DeviceCoordinates?.Length ?? 0) != coordinateCount), nameof(colors), "Device coordinate count must be the same for all colors");
}
@@ -154,16 +155,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.CoordinateCount;
- hashCode = (hashCode * 397) ^ (this.Prefix?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.Suffix?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ this.VendorFlags;
- hashCode = (hashCode * 397) ^ (this.Colors?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.CoordinateCount,
+ this.Prefix,
+ this.Suffix,
+ this.VendorFlags,
+ this.Colors);
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs
index 46719b80f7..9ec497d3be 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
public IccParametricCurveTagDataEntry(IccParametricCurve curve, IccProfileTag tagSignature)
: base(IccTypeSignature.ParametricCurve, tagSignature)
{
- this.Curve = curve;
+ this.Curve = curve ?? throw new ArgumentNullException(nameof(curve));
}
///
@@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Curve?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Curve);
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs
index da6fcd7a2a..ff69115267 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs
@@ -67,12 +67,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Descriptions?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Descriptions);
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs
index 51528a0736..c6cc0903c5 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs
@@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs
index e2cd5860bc..494aa2888b 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs
@@ -83,13 +83,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.ChannelCount.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.Curves?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.ChannelCount,
+ this.Curves);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs
index 0bf8abfcac..a073291ad1 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs
@@ -77,13 +77,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.Flags;
- hashCode = (hashCode * 397) ^ (this.Channels?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(this.Signature, this.Flags, this.Channels);
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs
index da557e644f..287f0efb0c 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs
@@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.SignatureData?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.SignatureData);
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs
index ca1e4c4915..8e6f4bc196 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs
@@ -166,16 +166,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.Ascii?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.Unicode?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.ScriptCode?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (int)this.UnicodeLanguageCode;
- hashCode = (hashCode * 397) ^ this.ScriptCodeCode.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.Ascii,
+ this.Unicode,
+ this.ScriptCode,
+ this.UnicodeLanguageCode,
+ this.ScriptCodeCode);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs
index f10712d96c..ab3b3fb6fb 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Text?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Text);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs
index 19430dc7b8..464cbb9e75 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs
index d9c093bda0..1e7a7f8a90 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs
index 8031919290..affdc8720b 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs
index 2973b9ae6b..36d48d6135 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs
@@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs
index 2391ce96a6..b2f2677575 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs
index eed4f97d47..510930e397 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs
@@ -86,14 +86,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ (this.UcrCurve?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.BgCurve?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.Description?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.UcrCurve,
+ this.BgCurve,
+ this.Description);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs
index da206a968b..a0089e7359 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs
@@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs
index 6be21dcc95..bd636d4f2c 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs
@@ -86,14 +86,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = base.GetHashCode();
- hashCode = (hashCode * 397) ^ this.IlluminantXyz.GetHashCode();
- hashCode = (hashCode * 397) ^ this.SurroundXyz.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.Illuminant;
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Signature,
+ this.IlluminantXyz,
+ this.SurroundXyz,
+ this.Illuminant);
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs
index 4878d96e4b..3f7cad3afe 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs
@@ -142,15 +142,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Values?.GetHashCode() ?? 0;
- hashCode = (hashCode * 397) ^ (int)this.DataType;
- hashCode = (hashCode * 397) ^ this.InputChannelCount;
- hashCode = (hashCode * 397) ^ this.OutputChannelCount;
- hashCode = (hashCode * 397) ^ (this.GridPointCount?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Values,
+ this.DataType,
+ this.InputChannelCount,
+ this.OutputChannelCount,
+ this.GridPointCount);
}
private bool EqualsValuesArray(IccClut other)
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs
index 56aa8b335d..8f273dd603 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs
@@ -102,14 +102,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Name.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Pcs1.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Pcs2.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Pcs3.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Name,
+ this.Pcs1,
+ this.Pcs2,
+ this.Pcs3);
}
///
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs
index 5b013fc2c1..b7cb5bc495 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs
@@ -80,27 +80,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public bool Equals(IccNamedColor other) =>
- this.Name == other.Name &&
- this.PcsCoordinates.SequenceEqual(other.PcsCoordinates) &&
- this.DeviceCoordinates.SequenceEqual(other.DeviceCoordinates);
+ public bool Equals(IccNamedColor other)
+ {
+ return this.Name.Equals(other.Name)
+ && this.PcsCoordinates.SequenceEqual(other.PcsCoordinates)
+ && this.DeviceCoordinates.SequenceEqual(other.DeviceCoordinates);
+ }
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Name.GetHashCode();
- hashCode = (hashCode * 397) ^ this.PcsCoordinates.GetHashCode();
- hashCode = (hashCode * 397) ^ this.DeviceCoordinates.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Name,
+ this.PcsCoordinates,
+ this.DeviceCoordinates);
}
///
- public override string ToString()
- {
- return this.Name;
- }
+ public override string ToString() => this.Name;
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs
index aad130b0de..745312f563 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs
@@ -73,15 +73,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
this.Size == other.Size;
///
- public override int GetHashCode()
- {
- return unchecked((int)(this.Offset ^ this.Size));
- }
+ public override int GetHashCode() => unchecked((int)(this.Offset ^ this.Size));
///
- public override string ToString()
- {
- return $"{this.Offset}; {this.Size}";
- }
+ public override string ToString() => $"{this.Offset}; {this.Size}";
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs
index 9db4bb9c48..7e7c527ead 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs
@@ -28,15 +28,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
IccLocalizedString[] deviceManufacturerInfo,
IccLocalizedString[] deviceModelInfo)
{
- Guard.NotNull(deviceManufacturerInfo, nameof(deviceManufacturerInfo));
- Guard.NotNull(deviceModelInfo, nameof(deviceModelInfo));
-
this.DeviceManufacturer = deviceManufacturer;
this.DeviceModel = deviceModel;
this.DeviceAttributes = deviceAttributes;
this.TechnologyInformation = technologyInformation;
- this.DeviceManufacturerInfo = deviceManufacturerInfo;
- this.DeviceModelInfo = deviceModelInfo;
+ this.DeviceManufacturerInfo = deviceManufacturerInfo ?? throw new ArgumentNullException(nameof(deviceManufacturerInfo));
+ this.DeviceModelInfo = deviceModelInfo ?? throw new ArgumentNullException(nameof(deviceModelInfo));
}
///
@@ -87,16 +84,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = (int)this.DeviceManufacturer;
- hashCode = (hashCode * 397) ^ (int)this.DeviceModel;
- hashCode = (hashCode * 397) ^ this.DeviceAttributes.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.TechnologyInformation;
- hashCode = (hashCode * 397) ^ (this.DeviceManufacturerInfo?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ (this.DeviceModelInfo?.GetHashCode() ?? 0);
- return hashCode;
- }
+ return HashCode.Combine(
+ this.DeviceManufacturer,
+ this.DeviceModel,
+ this.DeviceAttributes,
+ this.TechnologyInformation,
+ this.DeviceManufacturerInfo,
+ this.DeviceModelInfo);
}
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs
index 1389997109..f64d5409ae 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs
@@ -101,14 +101,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Part1.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Part2.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Part3.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Part4.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(
+ this.Part1,
+ this.Part2,
+ this.Part3,
+ this.Part4);
}
///
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs
index d5362ad706..ae451c5db8 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs
@@ -18,10 +18,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// Description of the profile
public IccProfileSequenceIdentifier(IccProfileId id, IccLocalizedString[] description)
{
- Guard.NotNull(description, nameof(description));
-
this.Id = id;
- this.Description = description;
+ this.Description = description ?? throw new ArgumentNullException(nameof(description));
}
///
@@ -46,12 +44,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- return (this.Id.GetHashCode() * 397) ^ (this.Description?.GetHashCode() ?? 0);
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.Id, this.Description);
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs
index d1da2366e7..8cae869925 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs
@@ -73,20 +73,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
this.MeasurementValue == other.MeasurementValue;
///
- public override int GetHashCode()
- {
- unchecked
- {
- int hashCode = this.DeviceCode.GetHashCode();
- hashCode = (hashCode * 397) ^ this.MeasurementValue.GetHashCode();
- return hashCode;
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.DeviceCode, this.MeasurementValue);
///
- public override string ToString()
- {
- return $"Code: {this.DeviceCode}; Value: {this.MeasurementValue}";
- }
+ public override string ToString() => $"Code: {this.DeviceCode}; Value: {this.MeasurementValue}";
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs
index 1c4ac8465c..e8885a66bf 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs
@@ -85,13 +85,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Frequency.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Angle.GetHashCode();
- hashCode = (hashCode * 397) ^ (int)this.SpotShape;
- return hashCode;
- }
+ return HashCode.Combine(this.Frequency, this.Angle, this.SpotShape);
}
///
diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs
index 04357dcf67..d93e068c52 100644
--- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs
+++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs
@@ -76,20 +76,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
///
public bool Equals(IccTagTableEntry other) =>
- this.Signature == other.Signature &&
- this.Offset == other.Offset &&
- this.DataSize == other.DataSize;
+ this.Signature.Equals(other.Signature) &&
+ this.Offset.Equals(other.Offset) &&
+ this.DataSize.Equals(other.DataSize);
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Signature.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Offset.GetHashCode();
- hashCode = (hashCode * 397) ^ this.DataSize.GetHashCode();
- return hashCode;
- }
+ return HashCode.Combine(this.Signature, this.Offset, this.DataSize);
}
///
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs
index 9207f046c4..96ff7da6f6 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -189,10 +190,6 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
- return HashHelpers.Combine(hash, this.B.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.B, this.G);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
index 293fe0acbf..86565731d2 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -200,11 +201,7 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
- return HashHelpers.Combine(hash, this.B.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.B, this.G);
///
public override string ToString() => $"Rgb24({this.R}, {this.G}, {this.B})";
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs
index 81497e5f18..eda116a46c 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs
@@ -189,11 +189,6 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- return HashHelpers.Combine(
- this.R.GetHashCode(),
- HashHelpers.Combine(this.G.GetHashCode(), this.B.GetHashCode()));
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs
index ff4c69d701..d65a5ade78 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs
@@ -197,11 +197,6 @@ namespace SixLabors.ImageSharp.PixelFormats
}
///
- public override int GetHashCode()
- {
- int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
- hash = HashHelpers.Combine(hash, this.B.GetHashCode());
- return HashHelpers.Combine(hash, this.A.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
index 139dbfa10f..e784e3b5a6 100644
--- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
+++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
index 5609e606d8..06c33d79c7 100644
--- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
+++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Primitives/ColorMatrix.cs b/src/ImageSharp/Primitives/ColorMatrix.cs
new file mode 100644
index 0000000000..af2e9465a9
--- /dev/null
+++ b/src/ImageSharp/Primitives/ColorMatrix.cs
@@ -0,0 +1,459 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+#pragma warning disable SA1117 // Parameters should be on same line or separate lines
+using System;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Primitives
+{
+ ///
+ /// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ColorMatrix : IEquatable
+ {
+ ///
+ /// Value at row 1, column 1 of the matrix.
+ ///
+ public float M11;
+
+ ///
+ /// Value at row 1, column 2 of the matrix.
+ ///
+ public float M12;
+
+ ///
+ /// Value at row 1, column 3 of the matrix.
+ ///
+ public float M13;
+
+ ///
+ /// Value at row 1, column 4 of the matrix.
+ ///
+ public float M14;
+
+ ///
+ /// Value at row 2, column 1 of the matrix.
+ ///
+ public float M21;
+
+ ///
+ /// Value at row 2, column 2 of the matrix.
+ ///
+ public float M22;
+
+ ///
+ /// Value at row 2, column 3 of the matrix.
+ ///
+ public float M23;
+
+ ///
+ /// Value at row 2, column 4 of the matrix.
+ ///
+ public float M24;
+
+ ///
+ /// Value at row 3, column 1 of the matrix.
+ ///
+ public float M31;
+
+ ///
+ /// Value at row 3, column 2 of the matrix.
+ ///
+ public float M32;
+
+ ///
+ /// Value at row 3, column 3 of the matrix.
+ ///
+ public float M33;
+
+ ///
+ /// Value at row 3, column 4 of the matrix.
+ ///
+ public float M34;
+
+ ///
+ /// Value at row 4, column 1 of the matrix.
+ ///
+ public float M41;
+
+ ///
+ /// Value at row 4, column 2 of the matrix.
+ ///
+ public float M42;
+
+ ///
+ /// Value at row 4, column 3 of the matrix.
+ ///
+ public float M43;
+
+ ///
+ /// Value at row 4, column 4 of the matrix.
+ ///
+ public float M44;
+
+ ///
+ /// Value at row 5, column 1 of the matrix.
+ ///
+ public float M51;
+
+ ///
+ /// Value at row 5, column 2 of the matrix.
+ ///
+ public float M52;
+
+ ///
+ /// Value at row 5, column 3 of the matrix.
+ ///
+ public float M53;
+
+ ///
+ /// Value at row 5, column 4 of the matrix.
+ ///
+ public float M54;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The value at row 1, column 1 of the matrix.
+ /// The value at row 1, column 2 of the matrix.
+ /// The value at row 1, column 3 of the matrix.
+ /// The value at row 1, column 4 of the matrix.
+ /// The value at row 2, column 1 of the matrix.
+ /// The value at row 2, column 2 of the matrix.
+ /// The value at row 2, column 3 of the matrix.
+ /// The value at row 2, column 4 of the matrix.
+ /// The value at row 3, column 1 of the matrix.
+ /// The value at row 3, column 2 of the matrix.
+ /// The value at row 3, column 3 of the matrix.
+ /// The value at row 3, column 4 of the matrix.
+ /// The value at row 4, column 1 of the matrix.
+ /// The value at row 4, column 2 of the matrix.
+ /// The value at row 4, column 3 of the matrix.
+ /// The value at row 4, column 4 of the matrix.
+ /// The value at row 5, column 1 of the matrix.
+ /// The value at row 5, column 2 of the matrix.
+ /// The value at row 5, column 3 of the matrix.
+ /// The value at row 5, column 4 of the matrix.
+ public ColorMatrix(float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44,
+ float m51, float m52, float m53, float m54)
+ {
+ this.M11 = m11;
+ this.M12 = m12;
+ this.M13 = m13;
+ this.M14 = m14;
+
+ this.M21 = m21;
+ this.M22 = m22;
+ this.M23 = m23;
+ this.M24 = m24;
+
+ this.M31 = m31;
+ this.M32 = m32;
+ this.M33 = m33;
+ this.M34 = m34;
+
+ this.M41 = m41;
+ this.M42 = m42;
+ this.M43 = m43;
+ this.M44 = m44;
+
+ this.M51 = m51;
+ this.M52 = m52;
+ this.M53 = m53;
+ this.M54 = m54;
+ }
+
+ ///
+ /// Gets the multiplicative identity matrix.
+ ///
+ public static ColorMatrix Identity { get; } =
+ new ColorMatrix(1F, 0F, 0F, 0F,
+ 0F, 1F, 0F, 0F,
+ 0F, 0F, 1F, 0F,
+ 0F, 0F, 0F, 1F,
+ 0F, 0F, 0F, 0F);
+
+ ///
+ /// Gets a value indicating whether the matrix is the identity matrix.
+ ///
+ public bool IsIdentity
+ {
+ get
+ {
+ // Check diagonal element first for early out.
+ return this.M11 == 1F && this.M22 == 1F && this.M33 == 1F && this.M44 == 1F
+ && this.M12 == 0F && this.M13 == 0F && this.M14 == 0F
+ && this.M21 == 0F && this.M23 == 0F && this.M24 == 0F
+ && this.M31 == 0F && this.M32 == 0F && this.M34 == 0F
+ && this.M41 == 0F && this.M42 == 0F && this.M43 == 0F
+ && this.M51 == 0F && this.M52 == 0F && this.M53 == 0F && this.M54 == 0F;
+ }
+ }
+
+ ///
+ /// Adds two matrices together.
+ ///
+ /// The first source matrix.
+ /// The second source matrix.
+ /// The resulting matrix.
+ public static ColorMatrix operator +(ColorMatrix value1, ColorMatrix value2)
+ {
+ ColorMatrix m;
+
+ m.M11 = value1.M11 + value2.M11;
+ m.M12 = value1.M12 + value2.M12;
+ m.M13 = value1.M13 + value2.M13;
+ m.M14 = value1.M14 + value2.M14;
+ m.M21 = value1.M21 + value2.M21;
+ m.M22 = value1.M22 + value2.M22;
+ m.M23 = value1.M23 + value2.M23;
+ m.M24 = value1.M24 + value2.M24;
+ m.M31 = value1.M31 + value2.M31;
+ m.M32 = value1.M32 + value2.M32;
+ m.M33 = value1.M33 + value2.M33;
+ m.M34 = value1.M34 + value2.M34;
+ m.M41 = value1.M41 + value2.M41;
+ m.M42 = value1.M42 + value2.M42;
+ m.M43 = value1.M43 + value2.M43;
+ m.M44 = value1.M44 + value2.M44;
+ m.M51 = value1.M51 + value2.M51;
+ m.M52 = value1.M52 + value2.M52;
+ m.M53 = value1.M53 + value2.M53;
+ m.M54 = value1.M54 + value2.M54;
+
+ return m;
+ }
+
+ ///
+ /// Subtracts the second matrix from the first.
+ ///
+ /// The first source matrix.
+ /// The second source matrix.
+ /// The result of the subtraction.
+ public static ColorMatrix operator -(ColorMatrix value1, ColorMatrix value2)
+ {
+ ColorMatrix m;
+
+ m.M11 = value1.M11 - value2.M11;
+ m.M12 = value1.M12 - value2.M12;
+ m.M13 = value1.M13 - value2.M13;
+ m.M14 = value1.M14 - value2.M14;
+ m.M21 = value1.M21 - value2.M21;
+ m.M22 = value1.M22 - value2.M22;
+ m.M23 = value1.M23 - value2.M23;
+ m.M24 = value1.M24 - value2.M24;
+ m.M31 = value1.M31 - value2.M31;
+ m.M32 = value1.M32 - value2.M32;
+ m.M33 = value1.M33 - value2.M33;
+ m.M34 = value1.M34 - value2.M34;
+ m.M41 = value1.M41 - value2.M41;
+ m.M42 = value1.M42 - value2.M42;
+ m.M43 = value1.M43 - value2.M43;
+ m.M44 = value1.M44 - value2.M44;
+ m.M51 = value1.M51 - value2.M51;
+ m.M52 = value1.M52 - value2.M52;
+ m.M53 = value1.M53 - value2.M53;
+ m.M54 = value1.M54 - value2.M54;
+
+ return m;
+ }
+
+ ///
+ /// Returns a new matrix with the negated elements of the given matrix.
+ ///
+ /// The source matrix.
+ /// The negated matrix.
+ public static unsafe ColorMatrix operator -(ColorMatrix value)
+ {
+ ColorMatrix m;
+
+ m.M11 = -value.M11;
+ m.M12 = -value.M12;
+ m.M13 = -value.M13;
+ m.M14 = -value.M14;
+ m.M21 = -value.M21;
+ m.M22 = -value.M22;
+ m.M23 = -value.M23;
+ m.M24 = -value.M24;
+ m.M31 = -value.M31;
+ m.M32 = -value.M32;
+ m.M33 = -value.M33;
+ m.M34 = -value.M34;
+ m.M41 = -value.M41;
+ m.M42 = -value.M42;
+ m.M43 = -value.M43;
+ m.M44 = -value.M44;
+ m.M51 = -value.M51;
+ m.M52 = -value.M52;
+ m.M53 = -value.M53;
+ m.M54 = -value.M54;
+
+ return m;
+ }
+
+ ///
+ /// Multiplies a matrix by another matrix.
+ ///
+ /// The first source matrix.
+ /// The second source matrix.
+ /// The result of the multiplication.
+ public static ColorMatrix operator *(ColorMatrix value1, ColorMatrix value2)
+ {
+ ColorMatrix m;
+
+ // First row
+ m.M11 = (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31) + (value1.M14 * value2.M41);
+ m.M12 = (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32) + (value1.M14 * value2.M42);
+ m.M13 = (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33) + (value1.M14 * value2.M43);
+ m.M14 = (value1.M11 * value2.M14) + (value1.M12 * value2.M24) + (value1.M13 * value2.M34) + (value1.M14 * value2.M44);
+
+ // Second row
+ m.M21 = (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31) + (value1.M24 * value2.M41);
+ m.M22 = (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32) + (value1.M24 * value2.M42);
+ m.M23 = (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33) + (value1.M24 * value2.M43);
+ m.M24 = (value1.M21 * value2.M14) + (value1.M22 * value2.M24) + (value1.M23 * value2.M34) + (value1.M24 * value2.M44);
+
+ // Third row
+ m.M31 = (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31) + (value1.M34 * value2.M41);
+ m.M32 = (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32) + (value1.M34 * value2.M42);
+ m.M33 = (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33) + (value1.M34 * value2.M43);
+ m.M34 = (value1.M31 * value2.M14) + (value1.M32 * value2.M24) + (value1.M33 * value2.M34) + (value1.M34 * value2.M44);
+
+ // Fourth row
+ m.M41 = (value1.M41 * value2.M11) + (value1.M42 * value2.M21) + (value1.M43 * value2.M31) + (value1.M44 * value2.M41);
+ m.M42 = (value1.M41 * value2.M12) + (value1.M42 * value2.M22) + (value1.M43 * value2.M32) + (value1.M44 * value2.M42);
+ m.M43 = (value1.M41 * value2.M13) + (value1.M42 * value2.M23) + (value1.M43 * value2.M33) + (value1.M44 * value2.M43);
+ m.M44 = (value1.M41 * value2.M14) + (value1.M42 * value2.M24) + (value1.M43 * value2.M34) + (value1.M44 * value2.M44);
+
+ // Fifth row
+ m.M51 = (value1.M51 * value2.M11) + (value1.M52 * value2.M21) + (value1.M53 * value2.M31) + (value1.M54 * value2.M41) + value2.M51;
+ m.M52 = (value1.M51 * value2.M12) + (value1.M52 * value2.M22) + (value1.M53 * value2.M32) + (value1.M54 * value2.M52) + value2.M52;
+ m.M53 = (value1.M51 * value2.M13) + (value1.M52 * value2.M23) + (value1.M53 * value2.M33) + (value1.M54 * value2.M53) + value2.M53;
+ m.M54 = (value1.M51 * value2.M14) + (value1.M52 * value2.M24) + (value1.M53 * value2.M34) + (value1.M54 * value2.M54) + value2.M54;
+
+ return m;
+ }
+
+ ///
+ /// Multiplies a matrix by a scalar value.
+ ///
+ /// The source matrix.
+ /// The scaling factor.
+ /// The scaled matrix.
+ public static ColorMatrix operator *(ColorMatrix value1, float value2)
+ {
+ ColorMatrix m;
+
+ m.M11 = value1.M11 * value2;
+ m.M12 = value1.M12 * value2;
+ m.M13 = value1.M13 * value2;
+ m.M14 = value1.M14 * value2;
+ m.M21 = value1.M21 * value2;
+ m.M22 = value1.M22 * value2;
+ m.M23 = value1.M23 * value2;
+ m.M24 = value1.M24 * value2;
+ m.M31 = value1.M31 * value2;
+ m.M32 = value1.M32 * value2;
+ m.M33 = value1.M33 * value2;
+ m.M34 = value1.M34 * value2;
+ m.M41 = value1.M41 * value2;
+ m.M42 = value1.M42 * value2;
+ m.M43 = value1.M43 * value2;
+ m.M44 = value1.M44 * value2;
+ m.M51 = value1.M51 * value2;
+ m.M52 = value1.M52 * value2;
+ m.M53 = value1.M53 * value2;
+ m.M54 = value1.M54 * value2;
+
+ return m;
+ }
+
+ ///
+ /// Returns a boolean indicating whether the given two matrices are equal.
+ ///
+ /// The first matrix to compare.
+ /// The second matrix to compare.
+ /// True if the given matrices are equal; False otherwise.
+ public static bool operator ==(ColorMatrix value1, ColorMatrix value2) => value1.Equals(value2);
+
+ ///
+ /// Returns a boolean indicating whether the given two matrices are not equal.
+ ///
+ /// The first matrix to compare.
+ /// The second matrix to compare.
+ /// True if the given matrices are equal; False otherwise.
+ public static bool operator !=(ColorMatrix value1, ColorMatrix value2) => !value1.Equals(value2);
+
+ ///
+ public override bool Equals(object obj) => obj is ColorMatrix matrix && this.Equals(matrix);
+
+ ///
+ public bool Equals(ColorMatrix other) =>
+ this.M11 == other.M11
+ && this.M12 == other.M12
+ && this.M13 == other.M13
+ && this.M14 == other.M14
+ && this.M21 == other.M21
+ && this.M22 == other.M22
+ && this.M23 == other.M23
+ && this.M24 == other.M24
+ && this.M31 == other.M31
+ && this.M32 == other.M32
+ && this.M33 == other.M33
+ && this.M34 == other.M34
+ && this.M41 == other.M41
+ && this.M42 == other.M42
+ && this.M43 == other.M43
+ && this.M44 == other.M44
+ && this.M51 == other.M51
+ && this.M52 == other.M52
+ && this.M53 == other.M53
+ && this.M54 == other.M54;
+
+ ///
+ public override int GetHashCode()
+ {
+ HashCode hash = default;
+ hash.Add(this.M11);
+ hash.Add(this.M12);
+ hash.Add(this.M13);
+ hash.Add(this.M14);
+ hash.Add(this.M21);
+ hash.Add(this.M22);
+ hash.Add(this.M23);
+ hash.Add(this.M24);
+ hash.Add(this.M31);
+ hash.Add(this.M32);
+ hash.Add(this.M33);
+ hash.Add(this.M34);
+ hash.Add(this.M41);
+ hash.Add(this.M42);
+ hash.Add(this.M43);
+ hash.Add(this.M44);
+ hash.Add(this.M51);
+ hash.Add(this.M52);
+ hash.Add(this.M53);
+ hash.Add(this.M54);
+ return hash.ToHashCode();
+ }
+
+ ///
+ public override string ToString()
+ {
+ CultureInfo ci = CultureInfo.CurrentCulture;
+
+ return string.Format(ci, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}",
+ this.M11.ToString(ci), this.M12.ToString(ci), this.M13.ToString(ci), this.M14.ToString(ci),
+ this.M21.ToString(ci), this.M22.ToString(ci), this.M23.ToString(ci), this.M24.ToString(ci),
+ this.M31.ToString(ci), this.M32.ToString(ci), this.M33.ToString(ci), this.M34.ToString(ci),
+ this.M41.ToString(ci), this.M42.ToString(ci), this.M43.ToString(ci), this.M44.ToString(ci),
+ this.M51.ToString(ci), this.M52.ToString(ci), this.M53.ToString(ci), this.M54.ToString(ci));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs
index 7cfa98ec1b..170292e29e 100644
--- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs
+++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs
@@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Primitives
/// The at the specified position.
public ref T this[int row, int column]
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
get
{
this.CheckCoordinates(row, column);
@@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Primitives
///
/// The representation on the source data.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator DenseMatrix(T[,] data) => new DenseMatrix(data);
///
@@ -135,9 +135,9 @@ namespace SixLabors.ImageSharp.Primitives
///
/// The representation on the source data.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
#pragma warning disable SA1008 // Opening parenthesis should be spaced correctly
- public static implicit operator T[,] (DenseMatrix data)
+ public static implicit operator T[,] (in DenseMatrix data)
#pragma warning restore SA1008 // Opening parenthesis should be spaced correctly
{
var result = new T[data.Rows, data.Columns];
@@ -154,17 +154,38 @@ namespace SixLabors.ImageSharp.Primitives
return result;
}
+ ///
+ /// Transposes the rows and columns of the dense matrix.
+ ///
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public DenseMatrix Transpose()
+ {
+ var result = new DenseMatrix(this.Rows, this.Columns);
+
+ for (int y = 0; y < this.Rows; y++)
+ {
+ for (int x = 0; x < this.Columns; x++)
+ {
+ ref T value = ref result[x, y];
+ value = this[y, x];
+ }
+ }
+
+ return result;
+ }
+
///
/// Fills the matrix with the given value
///
/// The value to fill each item with
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Fill(T value) => this.Span.Fill(value);
///
/// Clears the matrix setting each value to the default value for the element type
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Clear() => this.Span.Clear();
///
diff --git a/src/ImageSharp/Primitives/ValueSize.cs b/src/ImageSharp/Primitives/ValueSize.cs
index 8af9ba2e48..e5e086540d 100644
--- a/src/ImageSharp/Primitives/ValueSize.cs
+++ b/src/ImageSharp/Primitives/ValueSize.cs
@@ -133,9 +133,6 @@ namespace SixLabors.ImageSharp.Primitives
}
///
- public override int GetHashCode()
- {
- return HashHelpers.Combine(this.Value.GetHashCode(), this.Type.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.Value, this.Type);
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/FilterExtensions.cs
index bfae4ae654..70ac232863 100644
--- a/src/ImageSharp/Processing/FilterExtensions.cs
+++ b/src/ImageSharp/Processing/FilterExtensions.cs
@@ -1,8 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Processors.Filters;
using SixLabors.Primitives;
@@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing
/// The image this method extends.
/// The filter color matrix
/// The .
- public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix)
+ public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new FilterProcessor(matrix));
@@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
/// The structure that specifies the portion of the image object to alter.
///
/// The .
- public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix, Rectangle rectangle)
+ public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new FilterProcessor(matrix), rectangle);
}
diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs
index cf0d19ff85..9c725d0277 100644
--- a/src/ImageSharp/Processing/KnownFilterMatrices.cs
+++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs
@@ -2,19 +2,28 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Numerics;
-
+using SixLabors.ImageSharp.Primitives;
+
+// Many of these matrices are tranlated from Chromium project where
+// SkScalar[] is memory-mapped to a row-major matrix.
+// The following translates to our column-major form:
+//
+// | 0| 1| 2| 3| 4| |0|5|10|15| |M11|M12|M13|M14|
+// | 5| 6| 7| 8| 9| |1|6|11|16| |M21|M22|M23|M24|
+// |10|11|12|13|14| = |2|7|12|17| = |M31|M32|M33|M34|
+// |15|16|17|18|19| |3|8|13|18| |M41|M42|M43|M44|
+// |4|9|14|19| |M51|M52|M53|M54|
namespace SixLabors.ImageSharp.Processing
{
///
- /// A collection of known values for composing filters
+ /// A collection of known values for composing filters
///
public static class KnownFilterMatrices
{
///
/// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness
///
- public static Matrix4x4 AchromatomalyFilter { get; } = new Matrix4x4
+ public static ColorMatrix AchromatomalyFilter { get; } = new ColorMatrix
{
M11 = .618F,
M12 = .163F,
@@ -31,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing
///
/// Gets a filter recreating Achromatopsia (Monochrome) color blindness.
///
- public static Matrix4x4 AchromatopsiaFilter { get; } = new Matrix4x4
+ public static ColorMatrix AchromatopsiaFilter { get; } = new ColorMatrix
{
M11 = .299F,
M12 = .299F,
@@ -42,97 +51,97 @@ namespace SixLabors.ImageSharp.Processing
M31 = .114F,
M32 = .114F,
M33 = .114F,
- M44 = 1
+ M44 = 1F
};
///
/// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness.
///
- public static Matrix4x4 DeuteranomalyFilter { get; } = new Matrix4x4
+ public static ColorMatrix DeuteranomalyFilter { get; } = new ColorMatrix
{
- M11 = 0.8F,
- M12 = 0.258F,
- M21 = 0.2F,
- M22 = 0.742F,
- M23 = 0.142F,
- M33 = 0.858F,
- M44 = 1
+ M11 = .8F,
+ M12 = .258F,
+ M21 = .2F,
+ M22 = .742F,
+ M23 = .142F,
+ M33 = .858F,
+ M44 = 1F
};
///
/// Gets a filter recreating Deuteranopia (Green-Blind) color blindness.
///
- public static Matrix4x4 DeuteranopiaFilter { get; } = new Matrix4x4
+ public static ColorMatrix DeuteranopiaFilter { get; } = new ColorMatrix
{
- M11 = 0.625F,
- M12 = 0.7F,
- M21 = 0.375F,
- M22 = 0.3F,
- M23 = 0.3F,
- M33 = 0.7F,
- M44 = 1
+ M11 = .625F,
+ M12 = .7F,
+ M21 = .375F,
+ M22 = .3F,
+ M23 = .3F,
+ M33 = .7F,
+ M44 = 1F
};
///
/// Gets a filter recreating Protanomaly (Red-Weak) color blindness.
///
- public static Matrix4x4 ProtanomalyFilter { get; } = new Matrix4x4
+ public static ColorMatrix ProtanomalyFilter { get; } = new ColorMatrix
{
- M11 = 0.817F,
- M12 = 0.333F,
- M21 = 0.183F,
- M22 = 0.667F,
- M23 = 0.125F,
- M33 = 0.875F,
- M44 = 1
+ M11 = .817F,
+ M12 = .333F,
+ M21 = .183F,
+ M22 = .667F,
+ M23 = .125F,
+ M33 = .875F,
+ M44 = 1F
};
///
/// Gets a filter recreating Protanopia (Red-Blind) color blindness.
///
- public static Matrix4x4 ProtanopiaFilter { get; } = new Matrix4x4
+ public static ColorMatrix ProtanopiaFilter { get; } = new ColorMatrix
{
- M11 = 0.567F,
- M12 = 0.558F,
- M21 = 0.433F,
- M22 = 0.442F,
- M23 = 0.242F,
- M33 = 0.758F,
- M44 = 1
+ M11 = .567F,
+ M12 = .558F,
+ M21 = .433F,
+ M22 = .442F,
+ M23 = .242F,
+ M33 = .758F,
+ M44 = 1F
};
///
/// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness.
///
- public static Matrix4x4 TritanomalyFilter { get; } = new Matrix4x4
+ public static ColorMatrix TritanomalyFilter { get; } = new ColorMatrix
{
- M11 = 0.967F,
- M21 = 0.33F,
- M22 = 0.733F,
- M23 = 0.183F,
- M32 = 0.267F,
- M33 = 0.817F,
- M44 = 1
+ M11 = .967F,
+ M21 = .33F,
+ M22 = .733F,
+ M23 = .183F,
+ M32 = .267F,
+ M33 = .817F,
+ M44 = 1F
};
///
/// Gets a filter recreating Tritanopia (Blue-Blind) color blindness.
///
- public static Matrix4x4 TritanopiaFilter { get; } = new Matrix4x4
+ public static ColorMatrix TritanopiaFilter { get; } = new ColorMatrix
{
- M11 = 0.95F,
- M21 = 0.05F,
- M22 = 0.433F,
- M23 = 0.475F,
- M32 = 0.567F,
- M33 = 0.525F,
- M44 = 1
+ M11 = .95F,
+ M21 = .05F,
+ M22 = .433F,
+ M23 = .475F,
+ M32 = .567F,
+ M33 = .525F,
+ M44 = 1F
};
///
/// Gets an approximated black and white filter
///
- public static Matrix4x4 BlackWhiteFilter { get; } = new Matrix4x4()
+ public static ColorMatrix BlackWhiteFilter { get; } = new ColorMatrix()
{
M11 = 1.5F,
M12 = 1.5F,
@@ -143,24 +152,24 @@ namespace SixLabors.ImageSharp.Processing
M31 = 1.5F,
M32 = 1.5F,
M33 = 1.5F,
- M41 = -1F,
- M42 = -1F,
- M43 = -1F,
- M44 = 1
+ M44 = 1F,
+ M51 = -1F,
+ M52 = -1F,
+ M53 = -1F,
};
///
/// Gets a filter recreating an old Kodachrome camera effect.
///
- public static Matrix4x4 KodachromeFilter { get; } = new Matrix4x4
+ public static ColorMatrix KodachromeFilter { get; } = new ColorMatrix
{
- M11 = 0.7297023F,
- M22 = 0.6109577F,
- M33 = 0.597218F,
- M41 = 0.105F,
- M42 = 0.145F,
- M43 = 0.155F,
- M44 = 1
+ M11 = .7297023F,
+ M22 = .6109577F,
+ M33 = .597218F,
+ M44 = 1F,
+ M51 = .105F,
+ M52 = .145F,
+ M53 = .155F,
}
* CreateSaturateFilter(1.2F) * CreateContrastFilter(1.35F);
@@ -168,15 +177,15 @@ namespace SixLabors.ImageSharp.Processing
///
/// Gets a filter recreating an old Lomograph camera effect.
///
- public static Matrix4x4 LomographFilter { get; } = new Matrix4x4
+ public static ColorMatrix LomographFilter { get; } = new ColorMatrix
{
M11 = 1.5F,
M22 = 1.45F,
M33 = 1.16F,
- M41 = -.1F,
- M42 = -.02F,
- M43 = -.07F,
- M44 = 1
+ M44 = 1F,
+ M51 = -.1F,
+ M52 = -.02F,
+ M53 = -.07F,
}
* CreateSaturateFilter(1.1F) * CreateContrastFilter(1.33F);
@@ -184,21 +193,21 @@ namespace SixLabors.ImageSharp.Processing
///
/// Gets a filter recreating an old Polaroid camera effect.
///
- public static Matrix4x4 PolaroidFilter { get; } = new Matrix4x4
+ public static ColorMatrix PolaroidFilter { get; } = new ColorMatrix
{
M11 = 1.538F,
- M12 = -0.062F,
- M13 = -0.262F,
- M21 = -0.022F,
+ M12 = -.062F,
+ M13 = -.262F,
+ M21 = -.022F,
M22 = 1.578F,
- M23 = -0.022F,
+ M23 = -.022F,
M31 = .216F,
M32 = -.16F,
M33 = 1.5831F,
- M41 = 0.02F,
- M42 = -0.05F,
- M43 = -0.05F,
- M44 = 1
+ M44 = 1F,
+ M51 = .02F,
+ M52 = -.05F,
+ M53 = -.05F
};
///
@@ -209,18 +218,18 @@ namespace SixLabors.ImageSharp.Processing
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
///
/// The proportion of the conversion. Must be greater than or equal to 0.
- /// The
- public static Matrix4x4 CreateBrightnessFilter(float amount)
+ /// The
+ public static ColorMatrix CreateBrightnessFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
- return new Matrix4x4
+ return new ColorMatrix
{
M11 = amount,
M22 = amount,
M33 = amount,
- M44 = 1
+ M44 = 1F
};
}
@@ -232,23 +241,23 @@ namespace SixLabors.ImageSharp.Processing
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
///
/// The proportion of the conversion. Must be greater than or equal to 0.
- /// The
- public static Matrix4x4 CreateContrastFilter(float amount)
+ /// The
+ public static ColorMatrix CreateContrastFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
float contrast = (-.5F * amount) + .5F;
- return new Matrix4x4
+ return new ColorMatrix
{
M11 = amount,
M22 = amount,
M33 = amount,
- M41 = contrast,
- M42 = contrast,
- M43 = contrast,
- M44 = 1
+ M44 = 1F,
+ M51 = contrast,
+ M52 = contrast,
+ M53 = contrast
};
}
@@ -257,26 +266,27 @@ namespace SixLabors.ImageSharp.Processing
///
///
/// The proportion of the conversion. Must be between 0 and 1.
- /// The
- public static Matrix4x4 CreateGrayscaleBt601Filter(float amount)
+ /// The
+ public static ColorMatrix CreateGrayscaleBt601Filter(float amount)
{
- Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount));
amount = 1F - amount;
- // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
- return new Matrix4x4
- {
- M11 = .299F + (.701F * amount),
- M12 = .299F - (.299F * amount),
- M13 = .299F - (.299F * amount),
- M21 = .587F - (.587F * amount),
- M22 = .587F + (.413F * amount),
- M23 = .587F - (.587F * amount),
- M31 = .114F - (.114F * amount),
- M32 = .114F - (.114F * amount),
- M33 = .114F + (.886F * amount),
- M44 = 1
- };
+ ColorMatrix m = default;
+ m.M11 = .299F + (.701F * amount);
+ m.M21 = .587F - (.587F * amount);
+ m.M31 = 1F - (m.M11 + m.M21);
+
+ m.M12 = .299F - (.299F * amount);
+ m.M22 = .587F + (.2848F * amount);
+ m.M32 = 1F - (m.M12 + m.M22);
+
+ m.M13 = .299F - (.299F * amount);
+ m.M23 = .587F - (.587F * amount);
+ m.M33 = 1F - (m.M13 + m.M23);
+ m.M44 = 1F;
+
+ return m;
}
///
@@ -284,34 +294,36 @@ namespace SixLabors.ImageSharp.Processing
///
///
/// The proportion of the conversion. Must be between 0 and 1.
- /// The
- public static Matrix4x4 CreateGrayscaleBt709Filter(float amount)
+ /// The
+ public static ColorMatrix CreateGrayscaleBt709Filter(float amount)
{
- Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount));
amount = 1F - amount;
// https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
- return new Matrix4x4
- {
- M11 = .2126F + (.7874F * amount),
- M12 = .2126F - (.2126F * amount),
- M13 = .2126F - (.2126F * amount),
- M21 = .7152F - (.7152F * amount),
- M22 = .7152F + (.2848F * amount),
- M23 = .7152F - (.7152F * amount),
- M31 = .0722F - (.0722F * amount),
- M32 = .0722F - (.0722F * amount),
- M33 = .0722F + (.9278F * amount),
- M44 = 1
- };
+ ColorMatrix m = default;
+ m.M11 = .2126F + (.7874F * amount);
+ m.M21 = .7152F - (.7152F * amount);
+ m.M31 = 1F - (m.M11 + m.M21);
+
+ m.M12 = .2126F - (.2126F * amount);
+ m.M22 = .7152F + (.2848F * amount);
+ m.M32 = 1F - (m.M12 + m.M22);
+
+ m.M13 = .2126F - (.2126F * amount);
+ m.M23 = .7152F - (.7152F * amount);
+ m.M33 = 1F - (m.M13 + m.M23);
+ m.M44 = 1F;
+
+ return m;
}
///
/// Create a hue filter matrix using the given angle in degrees.
///
/// The angle of rotation in degrees.
- /// The
- public static Matrix4x4 CreateHueFilter(float degrees)
+ /// The
+ public static ColorMatrix CreateHueFilter(float degrees)
{
// Wrap the angle round at 360.
degrees %= 360;
@@ -329,18 +341,20 @@ namespace SixLabors.ImageSharp.Processing
// The matrix is set up to preserve the luminance of the image.
// See http://graficaobscura.com/matrix/index.html
// Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
- return new Matrix4x4
+ return new ColorMatrix
{
M11 = .213F + (cosRadian * .787F) - (sinRadian * .213F),
- M12 = .213F - (cosRadian * .213F) - (sinRadian * 0.143F),
- M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F),
M21 = .715F - (cosRadian * .715F) - (sinRadian * .715F),
- M22 = .715F + (cosRadian * .285F) + (sinRadian * 0.140F),
- M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F),
M31 = .072F - (cosRadian * .072F) + (sinRadian * .928F),
- M32 = .072F - (cosRadian * .072F) - (sinRadian * 0.283F),
+
+ M12 = .213F - (cosRadian * .213F) + (sinRadian * .143F),
+ M22 = .715F + (cosRadian * .285F) + (sinRadian * .140F),
+ M32 = .072F - (cosRadian * .072F) - (sinRadian * .283F),
+
+ M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F),
+ M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F),
M33 = .072F + (cosRadian * .928F) + (sinRadian * .072F),
- M44 = 1
+ M44 = 1F
};
}
@@ -348,23 +362,23 @@ namespace SixLabors.ImageSharp.Processing
/// Create an invert filter matrix using the given amount.
///
/// The proportion of the conversion. Must be between 0 and 1.
- /// The
- public static Matrix4x4 CreateInvertFilter(float amount)
+ /// The
+ public static ColorMatrix CreateInvertFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
float invert = 1F - (2F * amount);
- return new Matrix4x4
+ return new ColorMatrix
{
M11 = invert,
M22 = invert,
M33 = invert,
- M41 = amount,
- M42 = amount,
- M43 = amount,
- M44 = 1
+ M44 = 1F,
+ M51 = amount,
+ M52 = amount,
+ M53 = amount,
};
}
@@ -372,17 +386,17 @@ namespace SixLabors.ImageSharp.Processing
/// Create an opacity filter matrix using the given amount.
///
/// The proportion of the conversion. Must be between 0 and 1.
- /// The
- public static Matrix4x4 CreateOpacityFilter(float amount)
+ /// The
+ public static ColorMatrix CreateOpacityFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
- return new Matrix4x4
+ return new ColorMatrix
{
- M11 = 1,
- M22 = 1,
- M33 = 1,
+ M11 = 1F,
+ M22 = 1F,
+ M33 = 1F,
M44 = amount
};
}
@@ -395,25 +409,27 @@ namespace SixLabors.ImageSharp.Processing
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
///
/// The proportion of the conversion. Must be greater than or equal to 0.
- /// The
- public static Matrix4x4 CreateSaturateFilter(float amount)
+ /// The
+ public static ColorMatrix CreateSaturateFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
- return new Matrix4x4
- {
- M11 = .213F + (.787F * amount),
- M12 = .213F - (.213F * amount),
- M13 = .213F - (.213F * amount),
- M21 = .715F - (.715F * amount),
- M22 = .715F + (.285F * amount),
- M23 = .715F - (.715F * amount),
- M31 = 1F - ((.213F + (.787F * amount)) + (.715F - (.715F * amount))),
- M32 = 1F - ((.213F - (.213F * amount)) + (.715F + (.285F * amount))),
- M33 = 1F - ((.213F - (.213F * amount)) + (.715F - (.715F * amount))),
- M44 = 1
- };
+ ColorMatrix m = default;
+ m.M11 = .213F + (.787F * amount);
+ m.M21 = .715F - (.715F * amount);
+ m.M31 = 1F - (m.M11 + m.M21);
+
+ m.M12 = .213F - (.213F * amount);
+ m.M22 = .715F + (.285F * amount);
+ m.M32 = 1F - (m.M12 + m.M22);
+
+ m.M13 = .213F - (.213F * amount);
+ m.M23 = .715F - (.715F * amount);
+ m.M33 = 1F - (m.M13 + m.M23);
+ m.M44 = 1F;
+
+ return m;
}
///
@@ -421,25 +437,27 @@ namespace SixLabors.ImageSharp.Processing
/// The formula used matches the svg specification.
///
/// The proportion of the conversion. Must be between 0 and 1.
- /// The
- public static Matrix4x4 CreateSepiaFilter(float amount)
+ /// The
+ public static ColorMatrix CreateSepiaFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
- return new Matrix4x4
+ return new ColorMatrix
{
M11 = .393F + (.607F * amount),
- M12 = .349F - (.349F * amount),
- M13 = .272F - (.272F * amount),
M21 = .769F - (.769F * amount),
- M22 = .686F + (.314F * amount),
- M23 = .534F - (.534F * amount),
M31 = .189F - (.189F * amount),
+
+ M12 = .349F - (.349F * amount),
+ M22 = .686F + (.314F * amount),
M32 = .168F - (.168F * amount),
+
+ M13 = .272F - (.272F * amount),
+ M23 = .534F - (.534F * amount),
M33 = .131F + (.869F * amount),
- M44 = 1
+ M44 = 1F
};
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
index 38dc638b90..644d6c9e17 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
@@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
this.Radius = radius;
this.kernelSize = (radius * 2) + 1;
- this.KernelX = this.CreateBoxKernel(true);
- this.KernelY = this.CreateBoxKernel(false);
+ this.KernelX = this.CreateBoxKernel();
+ this.KernelY = this.KernelX.Transpose();
}
///
@@ -49,24 +49,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public DenseMatrix KernelY { get; }
///
- protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
- }
+ protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
///
/// Create a 1 dimensional Box kernel.
///
- /// Whether to calculate a horizontal kernel.
/// The
- private DenseMatrix CreateBoxKernel(bool horizontal)
+ private DenseMatrix CreateBoxKernel()
{
int size = this.kernelSize;
- DenseMatrix kernel = horizontal
- ? new DenseMatrix(size, 1)
- : new DenseMatrix(1, size);
+ var kernel = new DenseMatrix(size, 1);
- kernel.Fill(1.0F / size);
+ kernel.Fill(1F / size);
return kernel;
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
index 3045b9993f..b3bc15d391 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
@@ -4,7 +4,6 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
-using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
@@ -26,11 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// The 'sigma' value representing the weight of the blur.
public GaussianBlurProcessor(float sigma = 3F)
+ : this(sigma, (int)MathF.Ceiling(sigma * 3))
{
- this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1;
- this.Sigma = sigma;
- this.KernelX = this.CreateGaussianKernel(true);
- this.KernelY = this.CreateGaussianKernel(false);
+ // Kernel radius is calculated using the minimum viable value.
+ // http://chemaguerra.com/gaussian-filter-radius/
}
///
@@ -40,11 +38,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The 'radius' value representing the size of the area to sample.
///
public GaussianBlurProcessor(int radius)
+ : this(radius / 3F, radius)
{
- this.kernelSize = (radius * 2) + 1;
- this.Sigma = radius;
- this.KernelX = this.CreateGaussianKernel(true);
- this.KernelY = this.CreateGaussianKernel(false);
}
///
@@ -61,8 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
this.kernelSize = (radius * 2) + 1;
this.Sigma = sigma;
- this.KernelX = this.CreateGaussianKernel(true);
- this.KernelY = this.CreateGaussianKernel(false);
+ this.KernelX = this.CreateGaussianKernel();
+ this.KernelY = this.KernelX.Transpose();
}
///
@@ -82,22 +77,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
- }
+ => new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
///
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
///
- /// Whether to calculate a horizontal kernel.
/// The
- private DenseMatrix CreateGaussianKernel(bool horizontal)
+ private DenseMatrix CreateGaussianKernel()
{
int size = this.kernelSize;
float weight = this.Sigma;
- DenseMatrix kernel = horizontal
- ? new DenseMatrix(size, 1)
- : new DenseMatrix(1, size);
+ var kernel = new DenseMatrix(size, 1);
float sum = 0F;
float midpoint = (size - 1) / 2F;
@@ -107,30 +97,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float x = i - midpoint;
float gx = ImageMaths.Gaussian(x, weight);
sum += gx;
- if (horizontal)
- {
- kernel[0, i] = gx;
- }
- else
- {
- kernel[i, 0] = gx;
- }
+ kernel[0, i] = gx;
}
// Normalize kernel so that the sum of all weights equals 1
- if (horizontal)
- {
- for (int i = 0; i < size; i++)
- {
- kernel[0, i] = kernel[0, i] / sum;
- }
- }
- else
+ for (int i = 0; i < size; i++)
{
- for (int i = 0; i < size; i++)
- {
- kernel[i, 0] = kernel[i, 0] / sum;
- }
+ kernel[0, i] /= sum;
}
return kernel;
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
index 18963c73c0..786bf7757a 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
@@ -27,11 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The 'sigma' value representing the weight of the sharpening.
///
public GaussianSharpenProcessor(float sigma = 3F)
+ : this(sigma, (int)MathF.Ceiling(sigma * 3))
{
- this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1;
- this.Sigma = sigma;
- this.KernelX = this.CreateGaussianKernel(true);
- this.KernelY = this.CreateGaussianKernel(false);
+ // Kernel radius is calculated using the minimum viable value.
+ // http://chemaguerra.com/gaussian-filter-radius/
}
///
@@ -41,11 +40,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The 'radius' value representing the size of the area to sample.
///
public GaussianSharpenProcessor(int radius)
+ : this(radius / 3F, radius)
{
- this.kernelSize = (radius * 2) + 1;
- this.Sigma = radius;
- this.KernelX = this.CreateGaussianKernel(true);
- this.KernelY = this.CreateGaussianKernel(false);
}
///
@@ -62,8 +58,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
this.kernelSize = (radius * 2) + 1;
this.Sigma = sigma;
- this.KernelX = this.CreateGaussianKernel(true);
- this.KernelY = this.CreateGaussianKernel(false);
+ this.KernelX = this.CreateGaussianKernel();
+ this.KernelY = this.KernelX.Transpose();
}
///
@@ -83,91 +79,49 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
- }
+ => new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
///
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
///
- /// Whether to calculate a horizontal kernel.
/// The
- private DenseMatrix CreateGaussianKernel(bool horizontal)
+ private DenseMatrix CreateGaussianKernel()
{
int size = this.kernelSize;
float weight = this.Sigma;
- DenseMatrix kernel = horizontal
- ? new DenseMatrix(size, 1)
- : new DenseMatrix(1, size);
+ var kernel = new DenseMatrix(size, 1);
float sum = 0;
- float midpoint = (size - 1) / 2f;
+ float midpoint = (size - 1) / 2F;
for (int i = 0; i < size; i++)
{
float x = i - midpoint;
float gx = ImageMaths.Gaussian(x, weight);
sum += gx;
- if (horizontal)
- {
- kernel[0, i] = gx;
- }
- else
- {
- kernel[i, 0] = gx;
- }
+ kernel[0, i] = gx;
}
// Invert the kernel for sharpening.
int midpointRounded = (int)midpoint;
-
- if (horizontal)
+ for (int i = 0; i < size; i++)
{
- for (int i = 0; i < size; i++)
+ if (i == midpointRounded)
{
- if (i == midpointRounded)
- {
- // Calculate central value
- kernel[0, i] = (2F * sum) - kernel[0, i];
- }
- else
- {
- // invert value
- kernel[0, i] = -kernel[0, i];
- }
+ // Calculate central value
+ kernel[0, i] = (2F * sum) - kernel[0, i];
}
- }
- else
- {
- for (int i = 0; i < size; i++)
+ else
{
- if (i == midpointRounded)
- {
- // Calculate central value
- kernel[i, 0] = (2 * sum) - kernel[i, 0];
- }
- else
- {
- // invert value
- kernel[i, 0] = -kernel[i, 0];
- }
+ // invert value
+ kernel[0, i] = -kernel[0, i];
}
}
// Normalize kernel so that the sum of all weights equals 1
- if (horizontal)
- {
- for (int i = 0; i < size; i++)
- {
- kernel[0, i] /= sum;
- }
- }
- else
+ for (int i = 0; i < size; i++)
{
- for (int i = 0; i < size; i++)
- {
- kernel[i, 0] /= sum;
- }
+ kernel[0, i] /= sum;
}
return kernel;
diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
index f01865ec05..43f6c8a454 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
@@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
base.BeforeFrameApply(source, sourceRectangle, configuration);
// Lazy init paletteVector:
- if (this.paletteVector == null)
+ if (this.paletteVector is null)
{
this.paletteVector = new Vector4[this.Palette.Length];
PixelOperations.Instance.ToScaledVector4(configuration, this.Palette, this.paletteVector);
diff --git a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs
index b7bea2c746..13660d30ab 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs
@@ -43,7 +43,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
=> obj is PixelPair other && this.First.Equals(other.First) && this.Second.Equals(other.Second);
///
- public override int GetHashCode()
- => HashHelpers.Combine(this.First.GetHashCode(), this.Second.GetHashCode());
+ public override int GetHashCode() => HashCode.Combine(this.First, this.Second);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
index d3a44c066e..d6a32d8899 100644
--- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
@@ -3,16 +3,16 @@
using System;
using System.Numerics;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Filters
{
///
- /// Provides methods that accept a matrix to apply free-form filters to images.
+ /// Provides methods that accept a matrix to apply free-form filters to images.
///
/// The pixel format.
internal class FilterProcessor : ImageProcessor
@@ -22,38 +22,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// Initializes a new instance of the class.
///
/// The matrix used to apply the image filter
- public FilterProcessor(Matrix4x4 matrix)
- {
- this.Matrix = matrix;
- }
+ public FilterProcessor(ColorMatrix matrix) => this.Matrix = matrix;
///
- /// Gets the used to apply the image filter.
+ /// Gets the used to apply the image filter.
///
- public Matrix4x4 Matrix { get; }
+ public ColorMatrix Matrix { get; }
///
protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
+ int startX = interest.X;
- Matrix4x4 matrix = this.Matrix;
+ ColorMatrix matrix = this.Matrix;
- ParallelHelper.IterateRows(
+ ParallelHelper.IterateRowsWithTempBuffer(
interest,
configuration,
- rows =>
+ (rows, vectorBuffer) =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
- Span row = source.GetPixelRowSpan(y);
-
- for (int x = interest.X; x < interest.Right; x++)
- {
- ref TPixel pixel = ref row[x];
- var vector = Vector4.Transform(pixel.ToVector4(), matrix);
- pixel.FromVector4(vector);
- }
+ Span vectorSpan = vectorBuffer.Span;
+ int length = vectorSpan.Length;
+ Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length);
+ PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan);
+
+ Vector4Utils.Transform(vectorSpan, ref matrix);
+
+ PixelOperations.Instance.FromVector4(configuration, vectorSpan, rowSpan);
}
});
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
index 98d488a420..0b5627e19b 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
@@ -75,7 +75,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Convert from screen to world space.
Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 matrix);
- const float Epsilon = 0.0000001F;
if (this.Sampler is NearestNeighborResampler)
{
@@ -90,11 +89,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int x = 0; x < width; x++)
{
- var v3 = Vector3.Transform(new Vector3(x, y, 1), matrix);
-
- float z = MathF.Max(v3.Z, Epsilon);
- int px = (int)MathF.Round(v3.X / z);
- int py = (int)MathF.Round(v3.Y / z);
+ Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix);
+ int px = (int)MathF.Round(point.X);
+ int py = (int)MathF.Round(point.Y);
if (sourceRectangle.Contains(px, py))
{
@@ -127,9 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
- var v3 = Vector3.Transform(new Vector3(x, y, 1F), matrix);
- Vector2 point = new Vector2(v3.X, v3.Y) / MathF.Max(v3.Z, Epsilon);
-
+ Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix);
kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan);
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
index 24b15d3098..0ec8c83939 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
@@ -3,6 +3,7 @@
using System;
using System.Numerics;
+using System.Runtime.CompilerServices;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@@ -12,6 +13,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
internal static class TransformUtils
{
+ ///
+ /// Applies the projective transform against the given coordinates flattened into the 2D space.
+ ///
+ /// The "x" vector coordinate.
+ /// The "y" vector coordinate.
+ /// The transform matrix.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static Vector2 ProjectiveTransform2D(float x, float y, Matrix4x4 matrix)
+ {
+ const float Epsilon = 0.0000001F;
+ var v4 = Vector4.Transform(new Vector4(x, y, 0, 1F), matrix);
+ return new Vector2(v4.X, v4.Y) / MathF.Max(v4.W, Epsilon);
+ }
+
///
/// Creates a centered rotation matrix using the given rotation in degrees and the source size.
///
@@ -94,12 +110,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
Matrix4x4 matrix = Matrix4x4.Identity;
+ /*
+ * SkMatrix is layed out in the following manner:
+ *
+ * [ ScaleX SkewY Persp0 ]
+ * [ SkewX ScaleY Persp1 ]
+ * [ TransX TransY Persp2 ]
+ *
+ * When converting from Matrix4x4 to SkMatrix, the third row and
+ * column is dropped. When converting from SkMatrix to Matrix4x4
+ * the third row and column remain as identity:
+ *
+ * [ a b c ] [ a b 0 c ]
+ * [ d e f ] -> [ d e 0 f ]
+ * [ g h i ] [ 0 0 1 0 ]
+ * [ g h 0 i ]
+ */
switch (side)
{
case TaperSide.Left:
matrix.M11 = fraction;
matrix.M22 = fraction;
- matrix.M13 = (fraction - 1) / size.Width;
+ matrix.M14 = (fraction - 1) / size.Width;
switch (corner)
{
@@ -107,13 +139,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
break;
case TaperCorner.LeftOrTop:
- matrix.M12 = size.Height * matrix.M13;
- matrix.M32 = size.Height * (1 - fraction);
+ matrix.M12 = size.Height * matrix.M14;
+ matrix.M42 = size.Height * (1 - fraction);
break;
case TaperCorner.Both:
- matrix.M12 = size.Height * .5F * matrix.M13;
- matrix.M32 = size.Height * (1 - fraction) / 2;
+ matrix.M12 = size.Height * .5F * matrix.M14;
+ matrix.M42 = size.Height * (1 - fraction) / 2;
break;
}
@@ -122,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
case TaperSide.Top:
matrix.M11 = fraction;
matrix.M22 = fraction;
- matrix.M23 = (fraction - 1) / size.Height;
+ matrix.M24 = (fraction - 1) / size.Height;
switch (corner)
{
@@ -130,13 +162,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
break;
case TaperCorner.LeftOrTop:
- matrix.M21 = size.Width * matrix.M23;
- matrix.M31 = size.Width * (1 - fraction);
+ matrix.M21 = size.Width * matrix.M24;
+ matrix.M41 = size.Width * (1 - fraction);
break;
case TaperCorner.Both:
- matrix.M21 = size.Width * .5F * matrix.M23;
- matrix.M31 = size.Width * (1 - fraction) / 2;
+ matrix.M21 = size.Width * .5F * matrix.M24;
+ matrix.M41 = size.Width * (1 - fraction) * .5F;
break;
}
@@ -144,7 +176,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
case TaperSide.Right:
matrix.M11 = 1 / fraction;
- matrix.M13 = (1 - fraction) / (size.Width * fraction);
+ matrix.M14 = (1 - fraction) / (size.Width * fraction);
switch (corner)
{
@@ -152,11 +184,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
break;
case TaperCorner.LeftOrTop:
- matrix.M12 = size.Height * matrix.M13;
+ matrix.M12 = size.Height * matrix.M14;
break;
case TaperCorner.Both:
- matrix.M12 = size.Height * .5F * matrix.M13;
+ matrix.M12 = size.Height * .5F * matrix.M14;
break;
}
@@ -164,7 +196,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
case TaperSide.Bottom:
matrix.M22 = 1 / fraction;
- matrix.M23 = (1 - fraction) / (size.Height * fraction);
+ matrix.M24 = (1 - fraction) / (size.Height * fraction);
switch (corner)
{
@@ -172,11 +204,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
break;
case TaperCorner.LeftOrTop:
- matrix.M21 = size.Width * matrix.M23;
+ matrix.M21 = size.Width * matrix.M24;
break;
case TaperCorner.Both:
- matrix.M21 = size.Width * .5F * matrix.M23;
+ matrix.M21 = size.Width * .5F * matrix.M24;
break;
}
@@ -260,17 +292,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return rectangle;
}
- Vector2 GetVector(float x, float y)
- {
- const float Epsilon = 0.0000001F;
- var v3 = Vector3.Transform(new Vector3(x, y, 1F), matrix);
- return new Vector2(v3.X, v3.Y) / MathF.Max(v3.Z, Epsilon);
- }
-
- Vector2 tl = GetVector(rectangle.Left, rectangle.Top);
- Vector2 tr = GetVector(rectangle.Right, rectangle.Top);
- Vector2 bl = GetVector(rectangle.Left, rectangle.Bottom);
- Vector2 br = GetVector(rectangle.Right, rectangle.Bottom);
+ Vector2 tl = ProjectiveTransform2D(rectangle.Left, rectangle.Top, matrix);
+ Vector2 tr = ProjectiveTransform2D(rectangle.Right, rectangle.Top, matrix);
+ Vector2 bl = ProjectiveTransform2D(rectangle.Left, rectangle.Bottom, matrix);
+ Vector2 br = ProjectiveTransform2D(rectangle.Right, rectangle.Bottom, matrix);
return GetBoundingRectangle(tl, tr, bl, br);
}
diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs
index 327d3abd75..82e1dac6c4 100644
--- a/src/Shared/AssemblyInfo.Common.cs
+++ b/src/Shared/AssemblyInfo.Common.cs
@@ -5,32 +5,6 @@ using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyDescription("A cross-platform library for processing of image files; written in C#")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Six Labors")]
-[assembly: AssemblyProduct("SixLabors.ImageSharp")]
-[assembly: AssemblyCopyright("Copyright (c) Six Labors and contributors.")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: AssemblyInformationalVersion("1.0.0.0")]
-
// Ensure the internals can be built and tested.
[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")]
[assembly: InternalsVisibleTo("ImageSharp.Benchmarks")]
diff --git a/standards b/standards
new file mode 160000
index 0000000000..dd83f64963
--- /dev/null
+++ b/standards
@@ -0,0 +1 @@
+Subproject commit dd83f649638c6333984a757c01be6ec294e6b63c
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
index 05edd27919..8417b32f27 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
@@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
- JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output);
+ JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output, 255F, 128F);
}
[Benchmark]
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
- JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output);
+ JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output, 255F, 128F);
}
[Benchmark]
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
- JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output);
+ JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output, 255F, 128F);
}
private static Buffer2D[] CreateRandomValues(
diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
index 9ddfad7222..94349b20b6 100644
--- a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
+++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
@@ -1,5 +1,4 @@
using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Attributes.Jobs;
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath
{
diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
index 5c2fe81fa2..d5683673fe 100644
--- a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
+++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
@@ -1,5 +1,4 @@
using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Attributes.Jobs;
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath
{
diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs
index a8fea68661..3b288260c5 100644
--- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs
+++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs
@@ -4,7 +4,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Attributes.Jobs;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tuples;
diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
index a705c9bacb..f941203db1 100644
--- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
+++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
index 4d7b7de759..cb286cc286 100644
--- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
+++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
@@ -18,19 +18,13 @@
-
-
-
-
+
-
-
-
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
index 5f2de9f51e..5d7d35dd58 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
@@ -4,6 +4,9 @@
using System.IO;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
+using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
+
using Xunit;
// ReSharper disable InconsistentNaming
@@ -19,12 +22,14 @@ namespace SixLabors.ImageSharp.Tests
public static readonly string[] AllBmpFiles = All;
+ public static readonly string[] BitfieldsBmpFiles = BitFields;
+
public static readonly TheoryData RatioFiles =
new TheoryData
{
{ TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter },
{ TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter },
- { TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter }
+ { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter }
};
[Theory]
@@ -34,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image image = provider.GetImage(new BmpDecoder()))
{
- image.DebugSave(provider, "bmp");
+ image.DebugSave(provider);
if (TestEnvironment.IsWindows)
{
@@ -43,6 +48,108 @@ namespace SixLabors.ImageSharp.Tests
}
}
+ [Theory]
+ [WithFileCollection(nameof(BitfieldsBmpFiles), PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBitfields(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(Bit32Rgba, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider, new MagickReferenceDecoder());
+ }
+ }
+
+ [Theory]
+ [WithFile(Rgba321010102, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+
+ // Choosing large tolerance of 6.1 here, because for some reason with the MagickReferenceDecoder the alpha channel
+ // seems to be wrong. This bitmap has an alpha channel of two bits. In many cases this alpha channel has a value of 3,
+ // which should be remapped to 255 for RGBA32, but the magick decoder has a value of 191 set.
+ // The total difference without the alpha channel is still: 0.0204%
+ // Exporting the image as PNG with GIMP yields to the same result as the imagesharp implementation.
+ image.CompareToOriginal(provider, ImageComparer.TolerantPercentage(6.1f), new MagickReferenceDecoder());
+ }
+ }
+
+ [Theory]
+ [WithFile(WinBmpv2, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(WinBmpv3, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBmpv3(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(Rgba32bf56, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider, new MagickReferenceDecoder());
+ }
+ }
+
+ [Theory]
+ [WithFile(WinBmpv4, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(WinBmpv5, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider);
+ }
+ }
+
[Theory]
[WithFile(F, CommonNonDefaultPixelTypes)]
public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider)
@@ -50,7 +157,19 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image image = provider.GetImage(new BmpDecoder()))
{
- image.DebugSave(provider, "bmp");
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(Bit8Palette4, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecode4BytePerEntryPalette(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
image.CompareToOriginal(provider);
}
}
@@ -89,18 +208,33 @@ namespace SixLabors.ImageSharp.Tests
}
[Theory]
- [MemberData(nameof(RatioFiles))]
- public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
+ [WithFile(Os2v2Short, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecode_Os2v2XShortHeader(TestImageProvider provider)
+ where TPixel : struct, IPixel
{
- var testFile = TestFile.Create(imagePath);
- using (var stream = new MemoryStream(testFile.Bytes, false))
+ using (Image image = provider.GetImage(new BmpDecoder()))
{
- var decoder = new BmpDecoder();
- IImageInfo image = decoder.Identify(Configuration.Default, stream);
- ImageMetaData meta = image.MetaData;
- Assert.Equal(xResolution, meta.HorizontalResolution);
- Assert.Equal(yResolution, meta.VerticalResolution);
- Assert.Equal(resolutionUnit, meta.ResolutionUnits);
+ image.DebugSave(provider);
+
+ // TODO: Neither System.Drawing not MagickReferenceDecoder
+ // can correctly decode this file.
+ // image.CompareToOriginal(provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(Os2v2, PixelTypes.Rgba32)]
+ public void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ image.DebugSave(provider);
+
+ // TODO: System.Drawing can not decode this image. MagickReferenceDecoder can decode it,
+ // but i think incorrectly. I have loaded the image with GIMP and exported as PNG.
+ // The results are the same as the image sharp implementation.
+ // image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
index b9f855cf12..f818be8a91 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
@@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests
{
{ TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter },
{ TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter },
- { TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter }
+ { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter }
};
public static readonly TheoryData BmpBitsPerPixelFiles =
diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
index 158a085d5a..aed1edfbf0 100644
--- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
+++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
@@ -57,8 +57,7 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image image = file.CreateImage())
{
- var encoder = new PngEncoder { Quantizer = new WuQuantizer(KnownDiffusers.JarvisJudiceNinke, 256), ColorType = PngColorType.Palette };
- image.Save($"{path}/{file.FileName}.png", encoder);
+ image.Save($"{path}/{file.FileName}");
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 81c76390c1..7e7218c9dc 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.PrintLinearData(input);
Block8x8F dest = block;
- dest.NormalizeColorsInplace();
+ dest.NormalizeColorsInplace(255);
float[] array = new float[64];
dest.CopyTo(array);
@@ -253,11 +253,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Block8x8F source = CreateRandomFloatBlock(-200, 200, seed);
Block8x8F expected = source;
- expected.NormalizeColorsInplace();
+ expected.NormalizeColorsInplace(255);
expected.RoundInplace();
Block8x8F actual = source;
- actual.NormalizeColorsAndRoundInplaceAvx2();
+ actual.NormalizeColorsAndRoundInplaceAvx2(255);
this.Output.WriteLine(expected.ToString());
this.Output.WriteLine(actual.ToString());
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
index 8e30eb9e5d..caaad73c9f 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateRgbToYCbCrConversion(
- new JpegColorConverter.FromYCbCrBasic(),
+ new JpegColorConverter.FromYCbCrBasic(8),
3,
inputBufferLength,
resultBufferLength,
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter.ComponentValues values = CreateRandomValues(3, size, seed);
var result = new Vector4[size];
- JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result);
+ JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result, 255, 128);
for (int i = 0; i < size; i++)
{
@@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateRgbToYCbCrConversion(
- new JpegColorConverter.FromYCbCrSimd(),
+ new JpegColorConverter.FromYCbCrSimd(8),
3,
inputBufferLength,
resultBufferLength,
@@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
//JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s);
ValidateRgbToYCbCrConversion(
- new JpegColorConverter.FromYCbCrSimdAvx2(),
+ new JpegColorConverter.FromYCbCrSimdAvx2(8),
3,
inputBufferLength,
resultBufferLength,
@@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1);
var result = new Vector4[count];
- JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic();
+ JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd(8) : new JpegColorConverter.FromYCbCrBasic(8);
// Warm up:
converter.ConvertToRgba(values, result);
@@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
- var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk);
+ var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
@@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))]
public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed)
{
- var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale);
+ var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
@@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))]
public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed)
{
- var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB);
+ var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
@@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
- var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck);
+ var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
@@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int seed)
{
ValidateRgbToYCbCrConversion(
- JpegColorConverter.GetConverter(colorSpace),
+ JpegColorConverter.GetConverter(colorSpace,8),
componentCount,
inputBufferLength,
resultBufferLength,
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
index 73167a4b7e..1c9d207cd1 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
@@ -42,5 +42,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// TODO: We need a public ImageDecoderException class in ImageSharp!
Assert.ThrowsAny(() => provider.GetImage(JpegDecoder));
}
+
+ [Theory]
+ [WithFile(TestImages.Jpeg.Issues.InvalidJpegThrowsWrongException797, PixelTypes.Rgba32)]
+ public void LoadingImage_InvalidTagLength_ShouldThrow(TestImageProvider provider)
+ where TPixel : struct, IPixel => Assert.Throws(() => provider.GetImage());
+
+ [Theory]
+ [WithFile(TestImages.Jpeg.Issues.AccessViolationException798, PixelTypes.Rgba32)]
+ public void LoadingImage_BadHuffman_ShouldNotThrow(TestImageProvider provider)
+ where TPixel : struct, IPixel => Assert.NotNull(provider.GetImage());
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
index 40de25b30a..03f1826edd 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
@@ -28,7 +28,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.ExifResizeOutOfRange696,
TestImages.Jpeg.Issues.InvalidAPP0721,
TestImages.Jpeg.Issues.ExifGetString750Load,
- TestImages.Jpeg.Issues.ExifGetString750Transform
+ TestImages.Jpeg.Issues.ExifGetString750Transform,
+
+ // High depth images
+ TestImages.Jpeg.Baseline.Testorig12bit,
};
public static string[] ProgressiveTestJpegs =
diff --git a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs
index 9416be740a..f2e98b131a 100644
--- a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs
@@ -11,6 +11,8 @@ namespace SixLabors.ImageSharp.Tests.Helpers
{
public class Vector4UtilsTests
{
+ private readonly ApproximateFloatComparer ApproximateFloatComparer = new ApproximateFloatComparer(1e-6f);
+
[Theory]
[InlineData(0)]
[InlineData(1)]
@@ -23,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
Vector4Utils.Premultiply(source);
- Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ Assert.Equal(expected, source, this.ApproximateFloatComparer);
}
[Theory]
@@ -38,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
Vector4Utils.UnPremultiply(source);
- Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f));
+ Assert.Equal(expected, source, this.ApproximateFloatComparer);
}
}
}
diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
index 86c1a7a259..e20f1514cc 100644
--- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
+++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
@@ -27,16 +27,13 @@
-
-
+
-
-
-
-
+
+
diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs
new file mode 100644
index 0000000000..2fbe260ecd
--- /dev/null
+++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Globalization;
+using SixLabors.ImageSharp.Primitives;
+using SixLabors.ImageSharp.Processing;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Primitives
+{
+ public class ColorMatrixTests
+ {
+ private readonly ApproximateFloatComparer ApproximateFloatComparer = new ApproximateFloatComparer(1e-6f);
+
+ [Fact]
+ public void ColorMatrixIdentityIsCorrect()
+ {
+ ColorMatrix val = default;
+ val.M11 = val.M22 = val.M33 = val.M44 = 1F;
+
+ Assert.Equal(val, ColorMatrix.Identity, this.ApproximateFloatComparer);
+ }
+
+ [Fact]
+ public void ColorMatrixCanDetectIdentity()
+ {
+ ColorMatrix m = ColorMatrix.Identity;
+ Assert.True(m.IsIdentity);
+
+ m.M12 = 1F;
+ Assert.False(m.IsIdentity);
+ }
+
+ [Fact]
+ public void ColorMatrixEquality()
+ {
+ ColorMatrix m = KnownFilterMatrices.CreateHueFilter(45F);
+ ColorMatrix m2 = KnownFilterMatrices.CreateHueFilter(45F);
+ object obj = m2;
+
+ Assert.True(m.Equals(obj));
+ Assert.True(m.Equals(m2));
+ Assert.True(m == m2);
+ Assert.False(m != m2);
+ }
+
+ [Fact]
+ public void ColorMatrixMultiply()
+ {
+ ColorMatrix value1 = this.CreateAllTwos();
+ ColorMatrix value2 = this.CreateAllThrees();
+
+ ColorMatrix m;
+
+ // First row
+ m.M11 = (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31) + (value1.M14 * value2.M41);
+ m.M12 = (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32) + (value1.M14 * value2.M42);
+ m.M13 = (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33) + (value1.M14 * value2.M43);
+ m.M14 = (value1.M11 * value2.M14) + (value1.M12 * value2.M24) + (value1.M13 * value2.M34) + (value1.M14 * value2.M44);
+
+ // Second row
+ m.M21 = (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31) + (value1.M24 * value2.M41);
+ m.M22 = (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32) + (value1.M24 * value2.M42);
+ m.M23 = (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33) + (value1.M24 * value2.M43);
+ m.M24 = (value1.M21 * value2.M14) + (value1.M22 * value2.M24) + (value1.M23 * value2.M34) + (value1.M24 * value2.M44);
+
+ // Third row
+ m.M31 = (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31) + (value1.M34 * value2.M41);
+ m.M32 = (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32) + (value1.M34 * value2.M42);
+ m.M33 = (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33) + (value1.M34 * value2.M43);
+ m.M34 = (value1.M31 * value2.M14) + (value1.M32 * value2.M24) + (value1.M33 * value2.M34) + (value1.M34 * value2.M44);
+
+ // Fourth row
+ m.M41 = (value1.M41 * value2.M11) + (value1.M42 * value2.M21) + (value1.M43 * value2.M31) + (value1.M44 * value2.M41);
+ m.M42 = (value1.M41 * value2.M12) + (value1.M42 * value2.M22) + (value1.M43 * value2.M32) + (value1.M44 * value2.M42);
+ m.M43 = (value1.M41 * value2.M13) + (value1.M42 * value2.M23) + (value1.M43 * value2.M33) + (value1.M44 * value2.M43);
+ m.M44 = (value1.M41 * value2.M14) + (value1.M42 * value2.M24) + (value1.M43 * value2.M34) + (value1.M44 * value2.M44);
+
+ // Fifth row
+ m.M51 = (value1.M51 * value2.M11) + (value1.M52 * value2.M21) + (value1.M53 * value2.M31) + (value1.M54 * value2.M41) + value2.M51;
+ m.M52 = (value1.M51 * value2.M12) + (value1.M52 * value2.M22) + (value1.M53 * value2.M32) + (value1.M54 * value2.M52) + value2.M52;
+ m.M53 = (value1.M51 * value2.M13) + (value1.M52 * value2.M23) + (value1.M53 * value2.M33) + (value1.M54 * value2.M53) + value2.M53;
+ m.M54 = (value1.M51 * value2.M14) + (value1.M52 * value2.M24) + (value1.M53 * value2.M34) + (value1.M54 * value2.M54) + value2.M54;
+
+ Assert.Equal(m, value1 * value2, this.ApproximateFloatComparer);
+ }
+
+ [Fact]
+ public void ColorMatrixMultiplyScalar()
+ {
+ ColorMatrix m = this.CreateAllTwos();
+ Assert.Equal(this.CreateAllFours(), m * 2, this.ApproximateFloatComparer);
+ }
+
+ [Fact]
+ public void ColorMatrixSubtract()
+ {
+ ColorMatrix m = this.CreateAllOnes() + this.CreateAllTwos();
+ Assert.Equal(this.CreateAllThrees(), m);
+ }
+
+ [Fact]
+ public void ColorMatrixNegate()
+ {
+ ColorMatrix m = this.CreateAllOnes() * -1F;
+ Assert.Equal(m, -this.CreateAllOnes());
+ }
+
+ [Fact]
+ public void ColorMatrixAdd()
+ {
+ ColorMatrix m = this.CreateAllOnes() + this.CreateAllTwos();
+ Assert.Equal(this.CreateAllThrees(), m);
+ }
+
+ [Fact]
+ public void ColorMatrixHashCode()
+ {
+#if NETCOREAPP2_1
+ ColorMatrix m = KnownFilterMatrices.CreateBrightnessFilter(.5F);
+ HashCode hash = default;
+ hash.Add(m.M11);
+ hash.Add(m.M12);
+ hash.Add(m.M13);
+ hash.Add(m.M14);
+ hash.Add(m.M21);
+ hash.Add(m.M22);
+ hash.Add(m.M23);
+ hash.Add(m.M24);
+ hash.Add(m.M31);
+ hash.Add(m.M32);
+ hash.Add(m.M33);
+ hash.Add(m.M34);
+ hash.Add(m.M41);
+ hash.Add(m.M42);
+ hash.Add(m.M43);
+ hash.Add(m.M44);
+ hash.Add(m.M51);
+ hash.Add(m.M52);
+ hash.Add(m.M53);
+ hash.Add(m.M54);
+
+ Assert.Equal(hash.ToHashCode(), m.GetHashCode());
+#endif
+ }
+
+ [Fact]
+ public void ColorMatrixToString()
+ {
+ ColorMatrix m = KnownFilterMatrices.CreateBrightnessFilter(.5F);
+
+ CultureInfo ci = CultureInfo.CurrentCulture;
+
+ string expected = string.Format(ci, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}",
+ m.M11.ToString(ci), m.M12.ToString(ci), m.M13.ToString(ci), m.M14.ToString(ci),
+ m.M21.ToString(ci), m.M22.ToString(ci), m.M23.ToString(ci), m.M24.ToString(ci),
+ m.M31.ToString(ci), m.M32.ToString(ci), m.M33.ToString(ci), m.M34.ToString(ci),
+ m.M41.ToString(ci), m.M42.ToString(ci), m.M43.ToString(ci), m.M44.ToString(ci),
+ m.M51.ToString(ci), m.M52.ToString(ci), m.M53.ToString(ci), m.M54.ToString(ci));
+
+ Assert.Equal(expected, m.ToString());
+ }
+
+ private ColorMatrix CreateAllOnes()
+ {
+ return new ColorMatrix
+ {
+ M11 = 1F,
+ M12 = 1F,
+ M13 = 1F,
+ M14 = 1F,
+ M21 = 1F,
+ M22 = 1F,
+ M23 = 1F,
+ M24 = 1F,
+ M31 = 1F,
+ M32 = 1F,
+ M33 = 1F,
+ M34 = 1F,
+ M41 = 1F,
+ M42 = 1F,
+ M43 = 1F,
+ M44 = 1F,
+ M51 = 1F,
+ M52 = 1F,
+ M53 = 1F,
+ M54 = 1F
+ };
+ }
+
+ private ColorMatrix CreateAllTwos()
+ {
+ return new ColorMatrix
+ {
+ M11 = 2F,
+ M12 = 2F,
+ M13 = 2F,
+ M14 = 2F,
+ M21 = 2F,
+ M22 = 2F,
+ M23 = 2F,
+ M24 = 2F,
+ M31 = 2F,
+ M32 = 2F,
+ M33 = 2F,
+ M34 = 2F,
+ M41 = 2F,
+ M42 = 2F,
+ M43 = 2F,
+ M44 = 2F,
+ M51 = 2F,
+ M52 = 2F,
+ M53 = 2F,
+ M54 = 2F
+ };
+ }
+
+ private ColorMatrix CreateAllThrees()
+ {
+ return new ColorMatrix
+ {
+ M11 = 3F,
+ M12 = 3F,
+ M13 = 3F,
+ M14 = 3F,
+ M21 = 3F,
+ M22 = 3F,
+ M23 = 3F,
+ M24 = 3F,
+ M31 = 3F,
+ M32 = 3F,
+ M33 = 3F,
+ M34 = 3F,
+ M41 = 3F,
+ M42 = 3F,
+ M43 = 3F,
+ M44 = 3F,
+ M51 = 3F,
+ M52 = 3F,
+ M53 = 3F,
+ M54 = 3F
+ };
+ }
+
+ private ColorMatrix CreateAllFours()
+ {
+ return new ColorMatrix
+ {
+ M11 = 4F,
+ M12 = 4F,
+ M13 = 4F,
+ M14 = 4F,
+ M21 = 4F,
+ M22 = 4F,
+ M23 = 4F,
+ M24 = 4F,
+ M31 = 4F,
+ M32 = 4F,
+ M33 = 4F,
+ M34 = 4F,
+ M41 = 4F,
+ M42 = 4F,
+ M43 = 4F,
+ M44 = 4F,
+ M51 = 4F,
+ M52 = 4F,
+ M53 = 4F,
+ M54 = 4F
+ };
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs
index fa4862293f..0af8ae45f9 100644
--- a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs
+++ b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs
@@ -106,5 +106,29 @@ namespace SixLabors.ImageSharp.Tests.Primitives
Assert.Equal(0, dense.Data[i]);
}
}
+
+ [Fact]
+ public void DenseMatrixCorrectlyCasts()
+ {
+ float[,] actual = new DenseMatrix(FloydSteinbergMatrix);
+ Assert.Equal(FloydSteinbergMatrix, actual);
+ }
+
+ [Fact]
+ public void DenseMatrixCanTranspose()
+ {
+ var dense = new DenseMatrix(3, 1);
+ dense[0, 0] = 1;
+ dense[0, 1] = 2;
+ dense[0, 2] = 3;
+
+ DenseMatrix transposed = dense.Transpose();
+
+ Assert.Equal(dense.Columns, transposed.Rows);
+ Assert.Equal(dense.Rows, transposed.Columns);
+ Assert.Equal(1, transposed[0, 0]);
+ Assert.Equal(2, transposed[1, 0]);
+ Assert.Equal(3, transposed[2, 0]);
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs
index ed790cbac8..54a8dd4b7d 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs
@@ -8,10 +8,13 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
{
using SixLabors.ImageSharp.Processing;
+ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
[GroupOutput("Filters")]
public class BrightnessTest
{
+ private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.007F);
+
public static readonly TheoryData BrightnessValues
= new TheoryData
{
@@ -22,9 +25,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
[Theory]
[WithTestPatternImages(nameof(BrightnessValues), 48, 48, PixelTypes.Rgba32)]
public void ApplyBrightnessFilter(TestImageProvider provider, float value)
- where TPixel : struct, IPixel
- {
- provider.RunValidatingProcessorTest(ctx => ctx.Brightness(value), value);
- }
+ where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(ctx => ctx.Brightness(value), value, this.imageComparer);
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs
index 3d48e16ec9..8ac56655ea 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs
@@ -2,17 +2,19 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
using SixLabors.ImageSharp.Processing;
+ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
[GroupOutput("Filters")]
public class ColorBlindnessTest
{
+ private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.03F);
+
public static readonly TheoryData ColorBlindnessFilters
= new TheoryData
{
@@ -29,9 +31,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
[Theory]
[WithTestPatternImages(nameof(ColorBlindnessFilters), 48, 48, PixelTypes.Rgba32)]
public void ApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindnessMode colorBlindness)
- where TPixel : struct, IPixel
- {
- provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString());
- }
+ where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString(), this.imageComparer);
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs
index 479a3c33a2..68daa80eac 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs
@@ -1,16 +1,13 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
-
-using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
+ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
[GroupOutput("Filters")]
@@ -25,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
public void ApplyFilter(TestImageProvider provider)
where TPixel : struct, IPixel
{
- Matrix4x4 m = CreateCombinedTestFilterMatrix();
+ ColorMatrix m = CreateCombinedTestFilterMatrix();
provider.RunValidatingProcessorTest(x => x.Filter(m), comparer: ValidatorComparer);
}
@@ -35,18 +32,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
public void ApplyFilterInBox(TestImageProvider provider)
where TPixel : struct, IPixel
{
- Matrix4x4 m = CreateCombinedTestFilterMatrix();
+ ColorMatrix m = CreateCombinedTestFilterMatrix();
provider.RunRectangleConstrainedValidatingProcessorTest((x, b) => x.Filter(m, b), comparer: ValidatorComparer);
}
- private static Matrix4x4 CreateCombinedTestFilterMatrix()
+ private static ColorMatrix CreateCombinedTestFilterMatrix()
{
- Matrix4x4 brightness = KnownFilterMatrices.CreateBrightnessFilter(0.9F);
- Matrix4x4 hue = KnownFilterMatrices.CreateHueFilter(180F);
- Matrix4x4 saturation = KnownFilterMatrices.CreateSaturateFilter(1.5F);
- Matrix4x4 m = brightness * hue * saturation;
- return m;
+ ColorMatrix brightness = KnownFilterMatrices.CreateBrightnessFilter(0.9F);
+ ColorMatrix hue = KnownFilterMatrices.CreateHueFilter(180F);
+ ColorMatrix saturation = KnownFilterMatrices.CreateSaturateFilter(1.5F);
+ return brightness * hue * saturation;
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
index 08b2949139..5d3790f071 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
@@ -208,10 +208,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
var result = new TheoryData();
- string[] resamplerNames = typeof(KnownResamplers).GetProperties(BindingFlags.Public | BindingFlags.Static)
- .Select(p => p.Name)
- .Where(name => name != nameof(KnownResamplers.NearestNeighbor))
- .ToArray();
+ string[] resamplerNames = TestUtils.GetAllResamplerNames(false);
int[] dimensionVals =
{
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
index b4aff53e82..034b66ae9a 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
@@ -20,42 +20,82 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial };
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F);
-
- public static readonly TheoryData AllResamplers =
- new TheoryData
+
+ public static readonly string[] AllResamplerNames = TestUtils.GetAllResamplerNames();
+
+ public static readonly string[] SmokeTestResamplerNames =
{
- { "Bicubic", KnownResamplers.Bicubic },
- { "Triangle", KnownResamplers.Triangle},
- { "NearestNeighbor", KnownResamplers.NearestNeighbor },
- { "Box", KnownResamplers.Box },
- // { "Lanczos2", KnownResamplers.Lanczos2 }, TODO: Add expected file
- { "Lanczos3", KnownResamplers.Lanczos3 },
- { "Lanczos5", KnownResamplers.Lanczos5 },
- { "MitchellNetravali", KnownResamplers.MitchellNetravali },
- { "Lanczos8", KnownResamplers.Lanczos8 },
- { "Hermite", KnownResamplers.Hermite },
- { "Spline", KnownResamplers.Spline },
- { "Robidoux", KnownResamplers.Robidoux },
- { "RobidouxSharp", KnownResamplers.RobidouxSharp },
- { "Welch", KnownResamplers.Welch }
+ nameof(KnownResamplers.NearestNeighbor),
+ nameof(KnownResamplers.Bicubic),
+ nameof(KnownResamplers.Box),
+ nameof(KnownResamplers.Lanczos5),
};
[Theory]
- [WithTestPatternImages(nameof(AllResamplers), 100, 100, DefaultPixelType, 0.5f)]
- [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.5f)]
- [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.3f)]
- public void Resize_WorksWithAllResamplers(TestImageProvider provider, string name, IResampler sampler, float ratio)
+ [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), DefaultPixelType, 0.5f, null, null)]
+ [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 0.3f, null, null)]
+ [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 1.8f, null, null)]
+ [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 0.5f, null, null)]
+ [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 1f, null, null)]
+ [WithTestPatternImages(nameof(SmokeTestResamplerNames), 50, 50, DefaultPixelType, 8f, null, null)]
+ [WithTestPatternImages(nameof(SmokeTestResamplerNames), 201, 199, DefaultPixelType, null, 100, 99)]
+ [WithTestPatternImages(nameof(SmokeTestResamplerNames), 301, 1180, DefaultPixelType, null, 300, 480)]
+ [WithTestPatternImages(nameof(SmokeTestResamplerNames), 49, 80, DefaultPixelType, null, 301, 100)]
+ public void Resize_WorksWithAllResamplers(
+ TestImageProvider provider,
+ string samplerName,
+ float? ratio,
+ int? specificDestWidth,
+ int? specificDestHeight)
where TPixel : struct, IPixel
{
- using (Image image = provider.GetImage())
- {
- SizeF newSize = image.Size() * ratio;
- image.Mutate(x => x.Resize((Size)newSize, sampler, false));
- FormattableString details = $"{name}-{ratio.ToString(System.Globalization.CultureInfo.InvariantCulture)}";
+ IResampler sampler = TestUtils.GetResampler(samplerName);
- image.DebugSave(provider, details);
- image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.02f), provider, details);
- }
+ // NeirestNeighbourResampler is producing slightly different results With classic .NET framework on 32bit
+ // most likely because of differences in numeric behavior.
+ // The difference is well visible when comparing output for
+ // Resize_WorksWithAllResamplers_TestPattern301x1180_NearestNeighbor-300x480.png
+ // TODO: Should we investigate this?
+ bool allowHigherInaccuracy = !TestEnvironment.Is64BitProcess
+ && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion)
+ && sampler is NearestNeighborResampler;
+
+ var comparer = ImageComparer.TolerantPercentage(allowHigherInaccuracy ? 0.3f : 0.017f);
+
+ provider.RunValidatingProcessorTest(
+ ctx =>
+ {
+
+ SizeF newSize;
+ string destSizeInfo;
+ if (ratio.HasValue)
+ {
+ newSize = ctx.GetCurrentSize() * ratio.Value;
+ destSizeInfo = ratio.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ if (!specificDestWidth.HasValue || !specificDestHeight.HasValue)
+ {
+ throw new InvalidOperationException(
+ "invalid dimensional input for Resize_WorksWithAllResamplers!");
+ }
+
+ newSize = new SizeF(specificDestWidth.Value, specificDestHeight.Value);
+ destSizeInfo = $"{newSize.Width}x{newSize.Height}";
+ }
+
+ FormattableString testOutputDetails = $"{samplerName}-{destSizeInfo}";
+ ctx.Apply(
+ img => img.DebugSave(
+ provider,
+ $"{testOutputDetails}-ORIGINAL",
+ appendPixelTypeToFileName: false));
+ ctx.Resize((Size)newSize, sampler, false);
+ return testOutputDetails;
+ },
+ comparer,
+ appendPixelTypeToFileName: false);
}
[Theory]
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
index 1da660d222..056f66af7f 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs
@@ -111,7 +111,32 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image image = provider.GetImage())
{
Matrix4x4 matrix = Matrix4x4.Identity;
- matrix.M13 = 0.01F;
+ matrix.M14 = 0.01F;
+
+ ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
+ .AppendMatrix(matrix);
+
+ image.Mutate(i => i.Transform(builder));
+
+ image.DebugSave(provider);
+ image.CompareToReferenceOutput(TolerantComparer, provider);
+ }
+ }
+
+ [Theory]
+ [WithSolidFilledImages(290, 154, 0, 0, 255, PixelTypes.Rgba32)]
+ public void PerspectiveTransformMatchesCSS(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ // https://jsfiddle.net/dFrHS/545/
+ // https://github.com/SixLabors/ImageSharp/issues/787
+ using (Image image = provider.GetImage())
+ {
+ var matrix = new Matrix4x4(
+ 0.260987f, -0.434909f, 0, -0.0022184f,
+ 0.373196f, 0.949882f, 0, -0.000312129f,
+ 0, 0, 1, 0,
+ 52, 165, 0, 1);
ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder()
.AppendMatrix(matrix);
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 1144a3f7c0..d83fe4907d 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -135,12 +135,14 @@ namespace SixLabors.ImageSharp.Tests
public const string Testorig420 = "Jpg/baseline/testorig.jpg";
public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg";
public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg";
+ public const string Testorig12bit = "Jpg/baseline/testorig12.jpg";
public static readonly string[] All =
{
Cmyk, Ycck, Exif, Floorplan,
Calliphora, Turtle, GammaDalaiLamaGray,
- Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, Ratio1x1
+ Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444,
+ Ratio1x1, Testorig12bit
};
}
@@ -165,6 +167,8 @@ namespace SixLabors.ImageSharp.Tests
public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg";
public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg";
public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg";
+ public const string InvalidJpegThrowsWrongException797 = "Jpg/issues/Issue797-InvalidImage.jpg";
+ public const string AccessViolationException798 = "Jpg/issues/Issue798-AccessViolationException.jpg";
}
public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray();
@@ -191,7 +195,8 @@ namespace SixLabors.ImageSharp.Tests
public const string NegHeight = "Bmp/neg_height.bmp";
public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp";
public const string V5Header = "Bmp/BITMAPV5HEADER.bmp";
- public const string RLE = "Bmp/RunLengthEncoded.bmp";
+ public const string RLE8 = "Bmp/RunLengthEncoded.bmp";
+ public const string RLE4 = "Bmp/pal4rle.bmp";
public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp";
public const string Bit1 = "Bmp/pal1.bmp";
public const string Bit1Pal1 = "Bmp/pal1p1.bmp";
@@ -201,6 +206,37 @@ namespace SixLabors.ImageSharp.Tests
public const string Bit16 = "Bmp/test16.bmp";
public const string Bit16Inverted = "Bmp/test16-inverted.bmp";
public const string Bit32Rgb = "Bmp/rgb32.bmp";
+ public const string Bit32Rgba = "Bmp/rgba32.bmp";
+
+ // Note: This format can be called OS/2 BMPv1, or Windows BMPv2
+ public const string WinBmpv2 = "Bmp/pal8os2v1_winv2.bmp";
+
+ public const string WinBmpv3 = "Bmp/rgb24.bmp";
+ public const string WinBmpv4 = "Bmp/pal8v4.bmp";
+ public const string WinBmpv5 = "Bmp/pal8v5.bmp";
+ public const string Bit8Palette4 = "Bmp/pal8-0.bmp";
+ public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp";
+ public const string Os2v2 = "Bmp/pal8os2v2.bmp";
+
+ // Bitmap images with compression type BITFIELDS
+ public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp";
+ public const string Rgb32bf = "Bmp/rgb32bf.bmp";
+ public const string Rgb16bfdef = "Bmp/rgb16bfdef.bmp";
+ public const string Rgb16565 = "Bmp/rgb16-565.bmp";
+ public const string Rgb16565pal = "Bmp/rgb16-565pal.bmp";
+ public const string Issue735 = "Bmp/issue735.bmp";
+ public const string Rgba32bf56 = "Bmp/rgba32h56.bmp";
+ public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp";
+
+ public static readonly string[] BitFields
+ = {
+ Rgb32bfdef,
+ Rgb32bf,
+ Rgb16565,
+ Rgb16bfdef,
+ Rgb16565pal,
+ Issue735,
+ };
public static readonly string[] All
= {
@@ -208,7 +244,9 @@ namespace SixLabors.ImageSharp.Tests
F,
NegHeight,
CoreHeader,
- V5Header, RLE,
+ V5Header,
+ RLE4,
+ RLE8,
RLEInverted,
Bit1,
Bit1Pal1,
@@ -216,7 +254,8 @@ namespace SixLabors.ImageSharp.Tests
Bit8,
Bit8Inverted,
Bit16,
- Bit16Inverted
+ Bit16Inverted,
+ Bit32Rgb
};
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs
index 47ca6cccb2..872a935ffe 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Numerics;
+using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.Tests
{
@@ -11,8 +12,9 @@ namespace SixLabors.ImageSharp.Tests
///
internal readonly struct ApproximateFloatComparer :
IEqualityComparer,
+ IEqualityComparer,
IEqualityComparer,
- IEqualityComparer
+ IEqualityComparer
{
private readonly float Epsilon;
@@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests
/// Initializes a new instance of the class.
///
/// The comparison error difference epsilon to use.
- public ApproximateFloatComparer(float epsilon = 1f) => this.Epsilon = epsilon;
+ public ApproximateFloatComparer(float epsilon = 1F) => this.Epsilon = epsilon;
///
public bool Equals(float x, float y)
@@ -34,17 +36,29 @@ namespace SixLabors.ImageSharp.Tests
public int GetHashCode(float obj) => obj.GetHashCode();
///
- public bool Equals(Vector4 a, Vector4 b) => this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y) && this.Equals(a.Z, b.Z) && this.Equals(a.W, b.W);
+ public bool Equals(Vector2 x, Vector2 y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y);
///
- public int GetHashCode(Vector4 obj) => obj.GetHashCode();
+ public int GetHashCode(Vector2 obj) => obj.GetHashCode();
+
+ ///
+ public bool Equals(Vector4 x, Vector4 y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z) && this.Equals(x.W, y.W);
///
- public bool Equals(Vector2 a, Vector2 b) => this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y);
+ public int GetHashCode(Vector4 obj) => obj.GetHashCode();
- public int GetHashCode(Vector2 obj)
+ ///
+ public bool Equals(ColorMatrix x, ColorMatrix y)
{
- throw new System.NotImplementedException();
+ return
+ this.Equals(x.M11, y.M11) && this.Equals(x.M12, y.M12) && this.Equals(x.M13, y.M13) && this.Equals(x.M14, y.M14)
+ && this.Equals(x.M21, y.M21) && this.Equals(x.M22, y.M22) && this.Equals(x.M23, y.M23) && this.Equals(x.M24, y.M24)
+ && this.Equals(x.M31, y.M31) && this.Equals(x.M32, y.M32) && this.Equals(x.M33, y.M33) && this.Equals(x.M34, y.M34)
+ && this.Equals(x.M41, y.M41) && this.Equals(x.M42, y.M42) && this.Equals(x.M43, y.M43) && this.Equals(x.M44, y.M44)
+ && this.Equals(x.M51, y.M51) && this.Equals(x.M52, y.M52) && this.Equals(x.M53, y.M53) && this.Equals(x.M54, y.M54);
}
+
+ ///
+ public int GetHashCode(ColorMatrix obj) => obj.GetHashCode();
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs
index ec3254921f..6107154d0e 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs
@@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests
addedRows = memberItems.Select(x => x as object[]);
if (addedRows.Any(x => x == null))
{
- throw new ArgumentException($"Property {this.MemberName} on {this.MemberType ?? testMethod.DeclaringType} yielded an item that is not an object[]");
+ addedRows = memberItems.Select(x => new[] { x });
}
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
index cc09dc0573..17e5369d48 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
@@ -16,10 +16,9 @@ namespace SixLabors.ImageSharp.Tests
///
/// A test image provider that produces test patterns.
///
- ///
private class TestPatternProvider : BlankProvider
{
- static Dictionary> testImages = new Dictionary>();
+ static readonly Dictionary> TestImages = new Dictionary>();
public TestPatternProvider(int width, int height)
: base(width, height)
@@ -35,17 +34,17 @@ namespace SixLabors.ImageSharp.Tests
public override Image GetImage()
{
- lock (testImages)
+ lock (TestImages)
{
- if (!testImages.ContainsKey(this.SourceFileOrDescription))
+ if (!TestImages.ContainsKey(this.SourceFileOrDescription))
{
Image image = new Image(this.Width, this.Height);
DrawTestPattern(image);
- testImages.Add(this.SourceFileOrDescription, image);
+ TestImages.Add(this.SourceFileOrDescription, image);
}
}
- return testImages[this.SourceFileOrDescription].Clone();
+ return TestImages[this.SourceFileOrDescription].Clone();
}
///
@@ -202,6 +201,7 @@ namespace SixLabors.ImageSharp.Tests
Rgba32 t = new Rgba32(0);
for (int x = left; x < right; x++)
+ {
for (int y = top; y < bottom; y++)
{
t.PackedValue += stepsPerPixel;
@@ -210,6 +210,7 @@ namespace SixLabors.ImageSharp.Tests
c.FromVector4(v);
pixels[x, y] = c;
}
+ }
}
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
index 334b6552ac..7d06847223 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
@@ -34,8 +34,7 @@ namespace SixLabors.ImageSharp.Tests
{
string extension = Path.GetExtension(filePath);
- IImageFormat format = Configuration.ImageFormatsManager.FindFormatByFileExtension(extension);
- return format;
+ return Configuration.ImageFormatsManager.FindFormatByFileExtension(extension);
}
private static void ConfigureCodecs(
@@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests
cfg.ConfigureCodecs(
BmpFormat.Instance,
- SystemDrawingReferenceDecoder.Instance,
+ IsWindows ? (IImageDecoder)SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance,
bmpEncoder,
new BmpImageFormatDetector());
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs
index 5ea0bccf06..e51aa28d8f 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs
@@ -193,6 +193,45 @@ namespace SixLabors.ImageSharp.Tests
}
}
+ internal static void RunValidatingProcessorTest(
+ this TestImageProvider provider,
+ Func, FormattableString> processAndGetTestOutputDetails,
+ ImageComparer comparer = null,
+ bool appendPixelTypeToFileName = true,
+ bool appendSourceFileOrDescription = true)
+ where TPixel : struct, IPixel
+ {
+ if (comparer == null)
+ {
+ comparer = ImageComparer.TolerantPercentage(0.001f);
+ }
+
+ using (Image image = provider.GetImage())
+ {
+ FormattableString testOutputDetails = $"";
+ image.Mutate(
+ ctx => { testOutputDetails = processAndGetTestOutputDetails(ctx); }
+ );
+
+ image.DebugSave(
+ provider,
+ testOutputDetails,
+ appendPixelTypeToFileName: appendPixelTypeToFileName,
+ appendSourceFileOrDescription: appendSourceFileOrDescription);
+
+ // TODO: Investigate the cause of pixel inaccuracies under Linux
+ if (TestEnvironment.IsWindows)
+ {
+ image.CompareToReferenceOutput(
+ comparer,
+ provider,
+ testOutputDetails,
+ appendPixelTypeToFileName: appendPixelTypeToFileName,
+ appendSourceFileOrDescription: appendSourceFileOrDescription);
+ }
+ }
+ }
+
public static void RunValidatingProcessorTestOnWrappedMemoryImage(
this TestImageProvider provider,
Action> process,
@@ -297,5 +336,13 @@ namespace SixLabors.ImageSharp.Tests
return (IResampler)property.GetValue(null);
}
+
+ public static string[] GetAllResamplerNames(bool includeNearestNeighbour = true)
+ {
+ return typeof(KnownResamplers).GetProperties(BindingFlags.Public | BindingFlags.Static)
+ .Select(p => p.Name)
+ .Where(name => includeNearestNeighbour || name != nameof(KnownResamplers.NearestNeighbor))
+ .ToArray();
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs
index 30bb16c2a0..122234ae89 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs
@@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Tests
[Theory]
[InlineData("lol/foo.png", typeof(MagickReferenceDecoder))]
- [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))]
+ [InlineData("lol/Rofl.bmp", typeof(MagickReferenceDecoder))]
[InlineData("lol/Baz.JPG", typeof(JpegDecoder))]
[InlineData("lol/Baz.gif", typeof(GifDecoder))]
public void GetReferenceDecoder_ReturnsCorrectDecoders_Linux(string fileName, Type expectedDecoderType)
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
index a8140e39d4..cac7828e96 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
@@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using Xunit.Abstractions;
+// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
@@ -313,6 +314,17 @@ namespace SixLabors.ImageSharp.Tests
}
+ [Theory]
+ [WithTestPatternImages(49,20, PixelTypes.Rgba32)]
+ public void Use_WithTestPatternImages(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image img = provider.GetImage())
+ {
+ img.DebugSave(provider);
+ }
+ }
+
public static readonly TheoryData