Skip to content
Merged

Fkx #130

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -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']
5 changes: 4 additions & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: .NET build and test
env:
CURRENT_VERSION: 4.0.${{ github.run_number }}
CURRENT_VERSION: 4.1.${{ github.run_number }}
LAST_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}

on:
Expand All @@ -20,6 +20,7 @@ jobs:
with:
dotnet-version: |
8.0.x
9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand All @@ -38,6 +39,7 @@ jobs:
with:
dotnet-version: |
8.0.x
9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down Expand Up @@ -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
Expand Down
72 changes: 56 additions & 16 deletions ImageSharpCompare/ImageSharpCompare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,13 @@ public static bool ImagesHaveEqualSize(Image actualImage, Image expectedImage)
/// <param name="pathImageActual"></param>
/// <param name="pathImageExpected"></param>
/// <param name="resizeOption"></param>
/// <param name="pixelColorShiftTolerance"></param>
/// <returns>True if every pixel of actual is equal to expected</returns>
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);
}

/// <summary>
Expand All @@ -126,8 +127,9 @@ public static bool ImagesAreEqual(Stream actual, Stream expected, ResizeOption r
/// <param name="actual"></param>
/// <param name="expected"></param>
/// <param name="resizeOption"></param>
/// <param name="pixelColorShiftTolerance"></param>
/// <returns>True if every pixel of actual is equal to expected</returns>
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);

Expand All @@ -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
{
Expand All @@ -163,8 +165,9 @@ public static bool ImagesAreEqual(Image actual, Image expected, ResizeOption res
/// <param name="actual"></param>
/// <param name="expected"></param>
/// <param name="resizeOption"></param>
/// <param name="pixelColorShiftTolerance"></param>
/// <returns>True if every pixel of actual is equal to expected</returns>
public static bool ImagesAreEqual(Image<Rgb24> actual, Image<Rgb24> expected, ResizeOption resizeOption = ResizeOption.DontResize)
public static bool ImagesAreEqual(Image<Rgb24> actual, Image<Rgb24> expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0)
{
ArgumentNullException.ThrowIfNull(actual);

Expand All @@ -181,10 +184,21 @@ public static bool ImagesAreEqual(Image<Rgb24> actual, Image<Rgb24> 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;
}
}
}
}

Expand All @@ -194,7 +208,7 @@ public static bool ImagesAreEqual(Image<Rgb24> actual, Image<Rgb24> 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
{
Expand Down Expand Up @@ -350,7 +364,7 @@ public static ICompareResult CalcDiff(Image<Rgb24> actual, Image<Rgb24> 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;
}
}
Expand Down Expand Up @@ -477,7 +491,7 @@ public static ICompareResult CalcDiff(Image<Rgb24> actual, Image<Rgb24> expected
error += b;
}

absoluteError += (error > pixelColorShiftTolerance ? error : 0);
absoluteError += error > pixelColorShiftTolerance ? error : 0;
pixelErrorCount += error > pixelColorShiftTolerance ? 1 : 0;
}
}
Expand Down Expand Up @@ -663,14 +677,40 @@ public static Image CalcDiffMaskImage(Image<Rgb24> actual, Image<Rgb24> 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;
Expand Down
4 changes: 2 additions & 2 deletions ImageSharpCompare/ImageSharpCompare.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<RepositoryUrl>https://github.com/Codeuctivity/ImageSharp.Compare</RepositoryUrl>
Expand Down Expand Up @@ -45,7 +45,7 @@

<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.8" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.9.0.115408">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
24 changes: 24 additions & 0 deletions ImageSharpCompare/ImageSharpCompare.sln
Original file line number Diff line number Diff line change
@@ -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
40 changes: 28 additions & 12 deletions ImageSharpCompareTestNunit/ImageSharpCompareTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand All @@ -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");
Expand Down
16 changes: 10 additions & 6 deletions ImageSharpCompareTestNunit/ImageSharpCompareTestNunit.csproj
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="nunit" Version="4.2.2" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<PackageReference Include="nunit" Version="4.3.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.9.0.115408">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0">
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
</ItemGroup>

<ItemGroup>
Expand Down