diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..a1d7649
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: [Codeuctivity]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: stesee
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index de500a2..686f96f 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -20,6 +20,7 @@ jobs:
with:
dotnet-version: |
8.0.x
+ 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
@@ -38,6 +39,7 @@ jobs:
with:
dotnet-version: |
8.0.x
+ 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
@@ -67,6 +69,7 @@ jobs:
with:
dotnet-version: |
8.0.x
+ 9.0.x
- name: Check formatting
run: dotnet format --verify-no-changes
- name: Restore dependencies
diff --git a/ImageSharpCompare/ImageSharpCompare.cs b/ImageSharpCompare/ImageSharpCompare.cs
index 67b3723..80f8b8a 100644
--- a/ImageSharpCompare/ImageSharpCompare.cs
+++ b/ImageSharpCompare/ImageSharpCompare.cs
@@ -98,12 +98,13 @@ public static bool ImagesHaveEqualSize(Image actualImage, Image expectedImage)
///
///
///
+ ///
/// True if every pixel of actual is equal to expected
- public static bool ImagesAreEqual(string pathImageActual, string pathImageExpected, ResizeOption resizeOption = ResizeOption.DontResize)
+ public static bool ImagesAreEqual(string pathImageActual, string pathImageExpected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0)
{
using var actualImage = Image.Load(pathImageActual);
using var expectedImage = Image.Load(pathImageExpected);
- return ImagesAreEqual(actualImage, expectedImage, resizeOption);
+ return ImagesAreEqual(actualImage, expectedImage, resizeOption, pixelColorShiftTolerance);
}
///
@@ -126,8 +127,9 @@ public static bool ImagesAreEqual(Stream actual, Stream expected, ResizeOption r
///
///
///
+ ///
/// True if every pixel of actual is equal to expected
- public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption resizeOption = ResizeOption.DontResize)
+ public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0)
{
ArgumentNullException.ThrowIfNull(actual);
@@ -142,7 +144,7 @@ public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption res
actualPixelAccessibleImage = ImageSharpPixelTypeConverter.ToRgb24Image(actual, out ownsActual);
expectedPixelAccusableImage = ImageSharpPixelTypeConverter.ToRgb24Image(expected, out ownsExpected);
- return ImagesAreEqual(actualPixelAccessibleImage, expectedPixelAccusableImage, resizeOption);
+ return ImagesAreEqual(actualPixelAccessibleImage, expectedPixelAccusableImage, resizeOption, pixelColorShiftTolerance);
}
finally
{
@@ -163,8 +165,9 @@ public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption res
///
///
///
+ ///
/// True if every pixel of actual is equal to expected
- public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption resizeOption = ResizeOption.DontResize)
+ public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0)
{
ArgumentNullException.ThrowIfNull(actual);
@@ -181,10 +184,21 @@ public static bool ImagesAreEqual(Image actual, Image expected, Re
{
for (var y = 0; y < actual.Height; y++)
{
- if (!actual[x, y].Equals(expected[x, y]))
+ if (pixelColorShiftTolerance == 0 && !actual[x, y].Equals(expected[x, y]))
{
return false;
}
+ else if (pixelColorShiftTolerance > 0)
+ {
+ var actualPixel = actual[x, y];
+ var expectedPixel = expected[x, y];
+ if (Math.Abs(actualPixel.R - expectedPixel.R) > pixelColorShiftTolerance ||
+ Math.Abs(actualPixel.G - expectedPixel.G) > pixelColorShiftTolerance ||
+ Math.Abs(actualPixel.B - expectedPixel.B) > pixelColorShiftTolerance)
+ {
+ return false;
+ }
+ }
}
}
@@ -194,7 +208,7 @@ public static bool ImagesAreEqual(Image actual, Image expected, Re
var grown = GrowToSameDimension(actual, expected);
try
{
- return ImagesAreEqual(grown.Item1, grown.Item2, ResizeOption.DontResize);
+ return ImagesAreEqual(grown.Item1, grown.Item2, ResizeOption.DontResize, pixelColorShiftTolerance);
}
finally
{
@@ -350,7 +364,7 @@ public static ICompareResult CalcDiff(Image actual, Image expected
var g = Math.Abs(expectedPixel.G - actualPixel.G);
var b = Math.Abs(expectedPixel.B - actualPixel.B);
var sum = r + g + b;
- absoluteError += (sum > pixelColorShiftTolerance ? sum : 0);
+ absoluteError += sum > pixelColorShiftTolerance ? sum : 0;
pixelErrorCount += (sum > pixelColorShiftTolerance) ? 1 : 0;
}
}
@@ -477,7 +491,7 @@ public static ICompareResult CalcDiff(Image actual, Image expected
error += b;
}
- absoluteError += (error > pixelColorShiftTolerance ? error : 0);
+ absoluteError += error > pixelColorShiftTolerance ? error : 0;
pixelErrorCount += error > pixelColorShiftTolerance ? 1 : 0;
}
}
@@ -663,14 +677,40 @@ public static Image CalcDiffMaskImage(Image actual, Image expected
var actualPixel = actual[x, y];
var expectedPixel = expected[x, y];
- var pixel = new Rgb24
+ if (pixelColorShiftTolerance == 0)
{
- R = (byte)Math.Abs(actualPixel.R - expectedPixel.R),
- G = (byte)Math.Abs(actualPixel.G - expectedPixel.G),
- B = (byte)Math.Abs(actualPixel.B - expectedPixel.B)
- };
-
- maskImage[x, y] = pixel;
+ var pixel = new Rgb24
+ {
+ R = (byte)Math.Abs(actualPixel.R - expectedPixel.R),
+ G = (byte)Math.Abs(actualPixel.G - expectedPixel.G),
+ B = (byte)Math.Abs(actualPixel.B - expectedPixel.B)
+ };
+
+ maskImage[x, y] = pixel;
+ }
+ else
+ {
+ var r = Math.Abs(actualPixel.R - expectedPixel.R);
+ var g = Math.Abs(actualPixel.G - expectedPixel.G);
+ var b = Math.Abs(actualPixel.B - expectedPixel.B);
+
+ var error = r + g + b;
+ if (error <= pixelColorShiftTolerance)
+ {
+ r = 0;
+ g = 0;
+ b = 0;
+ }
+
+ var pixel = new Rgb24
+ {
+ R = (byte)r,
+ G = (byte)g,
+ B = (byte)b
+ };
+
+ maskImage[x, y] = pixel;
+ }
}
}
return maskImage;
diff --git a/ImageSharpCompare/ImageSharpCompare.csproj b/ImageSharpCompare/ImageSharpCompare.csproj
index 1432d6d..1d9d497 100644
--- a/ImageSharpCompare/ImageSharpCompare.csproj
+++ b/ImageSharpCompare/ImageSharpCompare.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net8.0;net9.0
true
true
https://github.com/Codeuctivity/ImageSharp.Compare
@@ -45,7 +45,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/ImageSharpCompare/ImageSharpCompare.sln b/ImageSharpCompare/ImageSharpCompare.sln
new file mode 100644
index 0000000..001bc91
--- /dev/null
+++ b/ImageSharpCompare/ImageSharpCompare.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharpCompare", "ImageSharpCompare.csproj", "{6D218566-E4FA-E901-D111-08AEB4065B5C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6D218566-E4FA-E901-D111-08AEB4065B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D218566-E4FA-E901-D111-08AEB4065B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D218566-E4FA-E901-D111-08AEB4065B5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D218566-E4FA-E901-D111-08AEB4065B5C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {239FA1C2-CD5C-4D95-BE3C-97B7B1BF4F3C}
+ EndGlobalSection
+EndGlobal
diff --git a/ImageSharpCompareTestNunit/ImageSharpCompareTest.cs b/ImageSharpCompareTestNunit/ImageSharpCompareTest.cs
index 26b59a5..60eea72 100644
--- a/ImageSharpCompareTestNunit/ImageSharpCompareTest.cs
+++ b/ImageSharpCompareTestNunit/ImageSharpCompareTest.cs
@@ -93,6 +93,18 @@ public void ShouldVerifyThatImagesWithDifferentSizeAreEqual(string pathActual, s
Assert.That(ImageSharpCompare.ImagesAreEqual(absolutePathActual, absolutePathExpected, resizeOption), Is.EqualTo(expectedResult));
}
+ [Test]
+ [TestCase(colorShift1, colorShift2, ResizeOption.DontResize, 0, false)]
+ [TestCase(colorShift1, colorShift2, ResizeOption.Resize, 0, false)]
+ [TestCase(colorShift1, colorShift2, ResizeOption.DontResize, 15, true)]
+ [TestCase(colorShift1, colorShift2, ResizeOption.Resize, 15, true)]
+ public void ShouldVerifyThatImagesWithColorShift(string pathActual, string pathExpected, ResizeOption resizeOption, int expectedColorShift, bool expectedResult)
+ {
+ var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual);
+ var absolutePathExpected = Path.Combine(AppContext.BaseDirectory, pathExpected);
+
+ Assert.That(ImageSharpCompare.ImagesAreEqual(absolutePathActual, absolutePathExpected, resizeOption, expectedColorShift), Is.EqualTo(expectedResult));
+ }
[Test]
[TestCase(jpg0Rgb24, jpg0Rgb24)]
@@ -157,7 +169,6 @@ public void ShouldVerifyThatImageSharpImagesAreEqualBgra5551(string pathActual,
}
[Test]
- [TestCase(jpg0Rgb24, png0Rgba32, 384538, 2.3789191061839596d, 140855, 87.139021553537404d, null, 0)]
[TestCase(jpg0Rgb24, png0Rgba32, 384538, 2.3789191061839596d, 140855, 87.139021553537404d, ResizeOption.DontResize, 0)]
[TestCase(jpg0Rgb24, png0Rgba32, 384538, 2.3789191061839596d, 140855, 87.139021553537404d, ResizeOption.Resize, 0)]
[TestCase(jpg1Rgb24, png1Rgba32, 382669, 2.3673566603152607d, 140893, 87.162530004206772d, ResizeOption.DontResize, 0)]
@@ -204,7 +215,6 @@ public void ShouldVerifyThatCalcDiffThrowsOnDifferentImageSizes(string pathPic1,
}
[Test]
- [TestCase(jpg0Rgb24, png0Rgba32, 384538, 2.3789191061839596d, 140855, 87.139021553537404d, null)]
[TestCase(jpg0Rgb24, png0Rgba32, 384538, 2.3789191061839596d, 140855, 87.139021553537404d, ResizeOption.DontResize)]
[TestCase(jpg0Rgb24, png0Rgba32, 384538, 2.3789191061839596d, 140855, 87.139021553537404d, ResizeOption.Resize)]
[TestCase(jpg1Rgb24, png1Rgba32, 382669, 2.3673566603152607d, 140893, 87.162530004206772d, ResizeOption.DontResize)]
@@ -230,26 +240,32 @@ public void ShouldVerifyThatImageStreamsAreSemiEqual(string pathPic1, string pat
Assert.That(diff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage");
}
- [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, null)]
- [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)]
- [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.Resize)]
- [TestCase(pngWhite2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)]
- [TestCase(pngBlack4x4px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize)]
- [TestCase(renderedForm1, renderedForm2, 0, 0, 0, 0, ResizeOption.Resize)]
- [TestCase(renderedForm2, renderedForm1, 0, 0, 0, 0, ResizeOption.Resize)]
- public void CalcDiffMaskImage(string pathPic1, string pathPic2, double expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption)
+ [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, 0, false)]
+ [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.Resize, 0, false)]
+ [TestCase(pngWhite2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0, false)]
+ [TestCase(pngBlack4x4px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0, false)]
+ [TestCase(renderedForm1, renderedForm2, 0, 0, 0, 0, ResizeOption.Resize, 0, false)]
+ [TestCase(renderedForm2, renderedForm1, 0, 0, 0, 0, ResizeOption.Resize, 0, false)]
+ [TestCase(colorShift1, colorShift1, 0, 0, 0, 0, ResizeOption.DontResize, 15, true)]
+ [TestCase(colorShift1, colorShift1, 0, 0, 0, 0, ResizeOption.Resize, 15, true)]
+ [TestCase(colorShift1, colorShift2, 0, 0, 0, 0, ResizeOption.Resize, 15, true)]
+ [TestCase(colorShift1, colorShift2, 0, 0, 0, 0, ResizeOption.DontResize, 15, true)]
+ [TestCase(colorShift1, colorShift2, 0, 0, 0, 0, ResizeOption.Resize, 14, false)]
+ [TestCase(colorShift1, colorShift2, 0, 0, 0, 0, ResizeOption.DontResize, 14, false)]
+ public void CalcDiffMaskImage(string pathPic1, string pathPic2, double expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int expectedColorShift, bool expectMaskToBeBlack)
{
var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1);
var absolutePathPic2 = Path.Combine(AppContext.BaseDirectory, pathPic2);
var differenceMask = Path.GetTempFileName() + "differenceMask.png";
using (var fileStreamDifferenceMask = File.Create(differenceMask))
- using (var maskImage = ImageSharpCompare.CalcDiffMaskImage(absolutePathPic1, absolutePathPic2, resizeOption))
+ using (var maskImage = ImageSharpCompare.CalcDiffMaskImage(absolutePathPic1, absolutePathPic2, resizeOption, expectedColorShift))
{
ImageExtensions.SaveAsPng(maskImage, fileStreamDifferenceMask);
+ Assert.That(IsImageEntirelyBlack(maskImage), Is.EqualTo(expectMaskToBeBlack));
}
- var maskedDiff = ImageSharpCompare.CalcDiff(absolutePathPic1, absolutePathPic2, differenceMask, resizeOption);
+ var maskedDiff = ImageSharpCompare.CalcDiff(absolutePathPic1, absolutePathPic2, differenceMask, resizeOption, expectedColorShift);
File.Delete(differenceMask);
Assert.That(maskedDiff.AbsoluteError, Is.EqualTo(expectedAbsoluteError), "AbsoluteError");
diff --git a/ImageSharpCompareTestNunit/ImageSharpCompareTestNunit.csproj b/ImageSharpCompareTestNunit/ImageSharpCompareTestNunit.csproj
index c82f334..e463c48 100644
--- a/ImageSharpCompareTestNunit/ImageSharpCompareTestNunit.csproj
+++ b/ImageSharpCompareTestNunit/ImageSharpCompareTestNunit.csproj
@@ -1,27 +1,31 @@
- net8.0
+ net8.0;net9.0
false
enable
true
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+