Skip to content
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: # Replace with a single Buy Me a Coffee username
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: 2.0.${{ github.run_number }}
CURRENT_VERSION: 2.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: Check formatting
run: dotnet format --verify-no-changes
- name: Restore dependencies
Expand All @@ -40,6 +41,7 @@ jobs:
with:
dotnet-version: |
8.0.x
9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down Expand Up @@ -69,6 +71,7 @@ jobs:
with:
dotnet-version: |
8.0.x
9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
69 changes: 61 additions & 8 deletions SkiaSharpCompare/SkiaSharpCompare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,25 @@ public static bool ImagesAreEqual(SKBitmap actual, SKBitmap expected, ResizeOpti
{
for (var y = 0; y < actual.Height; y++)
{
if (!actual.GetPixel(x, y).Equals(expected.GetPixel(x, y)))
if (pixelColorShiftTolerance == 0 && !actual.GetPixel(x, y).Equals(expected.GetPixel(x, y)))
{
return false;
}
else
{
var actualPixel = actual.GetPixel(x, y);
var expectedPixel = expected.GetPixel(x, y);

var r = Math.Abs(expectedPixel.Red - actualPixel.Red);
var g = Math.Abs(expectedPixel.Green - actualPixel.Green);
var b = Math.Abs(expectedPixel.Blue - actualPixel.Blue);
var sum = r + g + b;

if (sum > pixelColorShiftTolerance)
{
return false;
}
}
}
}

Expand Down Expand Up @@ -472,7 +487,26 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, Res
var blue = (byte)Math.Abs(actualPixel.Blue - expectedPixel.Blue);
var pixel = new SKColor(red, green, blue);

maskImage.SetPixel(x, y, pixel);
if (pixelColorShiftTolerance == 0)
{
maskImage.SetPixel(x, y, pixel);
}
else
{
var r = Math.Abs(expectedPixel.Red - actualPixel.Red);
var g = Math.Abs(expectedPixel.Green - actualPixel.Green);
var b = Math.Abs(expectedPixel.Blue - actualPixel.Blue);
var sum = r + g + b;

if (sum > pixelColorShiftTolerance)
{
maskImage.SetPixel(x, y, pixel);
}
else
{
maskImage.SetPixel(x, y, 0);
}
}
}
}
return maskImage;
Expand Down Expand Up @@ -527,7 +561,26 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, SKB
var blue = (byte)(Math.Abs(actualPixel.Blue - expectedPixel.Blue) - maskPixel.Blue);
var pixel = new SKColor(red, green, blue);

maskImage.SetPixel(x, y, pixel);
if (pixelColorShiftTolerance == 0)
{
maskImage.SetPixel(x, y, pixel);
}
else
{
var r = Math.Abs(expectedPixel.Red - actualPixel.Red);
var g = Math.Abs(expectedPixel.Green - actualPixel.Green);
var b = Math.Abs(expectedPixel.Blue - actualPixel.Blue);
var sum = r + g + b;

if (sum > pixelColorShiftTolerance)
{
maskImage.SetPixel(x, y, pixel);
}
else
{
maskImage.SetPixel(x, y, 0);
}
}
}
}
return maskImage;
Expand All @@ -551,8 +604,8 @@ private static (SKBitmap, SKBitmap) GrowToSameDimension(SKBitmap actual, SKBitma
var biggestWidth = actual.Width > expected.Width ? actual.Width : expected.Width;
var biggestHeight = actual.Height > expected.Height ? actual.Height : expected.Height;
var skSize = new SKSizeI(biggestWidth, biggestHeight);
var grownExpected = expected.Resize(skSize, SKFilterQuality.None);
var grownActual = actual.Resize(skSize, SKFilterQuality.None);
var grownExpected = expected.Resize(skSize, SKSamplingOptions.Default);
var grownActual = actual.Resize(skSize, SKSamplingOptions.Default);

return (grownActual, grownExpected);
}
Expand All @@ -564,9 +617,9 @@ private static (SKBitmap, SKBitmap, SKBitmap) GrowToSameDimension(SKBitmap actua
var biggestHeight = actual.Height > expected.Height ? actual.Height : expected.Height;
biggestHeight = biggestHeight > mask.Height ? biggestHeight : mask.Height;
var skSize = new SKSizeI(biggestWidth, biggestHeight);
var grownExpected = expected.Resize(skSize, SKFilterQuality.None);
var grownActual = actual.Resize(skSize, SKFilterQuality.None);
var grownMask = mask.Resize(skSize, SKFilterQuality.None);
var grownExpected = expected.Resize(skSize, SKSamplingOptions.Default);
var grownActual = actual.Resize(skSize, SKSamplingOptions.Default);
var grownMask = mask.Resize(skSize, SKSamplingOptions.Default);

return (grownActual, grownExpected, grownMask);
}
Expand Down
10 changes: 5 additions & 5 deletions SkiaSharpCompare/SkiaSharpCompare.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<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/SkiaSharp.Compare</RepositoryUrl>
Expand Down Expand Up @@ -46,9 +46,9 @@

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
<PackageReference Include="SkiaSharp" Version="3.116.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.116.1" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.18.0.83559">
<PackageReference Include="SkiaSharp" Version="3.119.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.8.0.113526">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
50 changes: 36 additions & 14 deletions SkiaSharpCompareTestNunit/SkiaSharpCompareTestNunit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,15 @@ public void ShouldVerifyThatImageStreamsSizeAreEqual(string pathActual, string p
}

[Test]
[TestCase(jpg0Rgb24, jpg0Rgb24)]
[TestCase(png0Rgba32, png0Rgba32)]
public void ShouldVerifyThatImagesAreEqual(string pathActual, string pathExpected)
[TestCase(jpg0Rgb24, jpg0Rgb24, 0)]
[TestCase(png0Rgba32, png0Rgba32, 0)]
[TestCase(colorShift1, colorShift2, 15)]
public void ShouldVerifyThatImagesAreEqual(string pathActual, string pathExpected, int pixelColorShiftTolerance)
{
var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual);
var absolutePathExpected = Path.Combine(AppContext.BaseDirectory, pathExpected);

Assert.That(Compare.ImagesAreEqual(absolutePathActual, absolutePathExpected), Is.True);
Assert.That(Compare.ImagesAreEqual(absolutePathActual, absolutePathExpected, pixelColorShiftTolerance: pixelColorShiftTolerance), Is.True);
}

[Test]
Expand Down Expand Up @@ -209,7 +210,7 @@ public void Diffmask(string pathPic1, string pathPic2, int expectedMeanError, in
using (var fileStreamDifferenceMask = File.Create(differenceMask))
using (var maskImage = Compare.CalcDiffMaskImage(absolutePathPic1, absolutePathPic2, resizeOption))
{
SaveAsPng(maskImage, fileStreamDifferenceMask);
IntegrationTest.SaveAsPng(maskImage, fileStreamDifferenceMask);
}

var maskedDiff = Compare.CalcDiff(absolutePathPic1, absolutePathPic2, differenceMask, resizeOption);
Expand All @@ -221,7 +222,7 @@ public void Diffmask(string pathPic1, string pathPic2, int expectedMeanError, in
Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage");
}

private void SaveAsPng(SKBitmap maskImage, FileStream fileStreamDifferenceMask)
private static void SaveAsPng(SKBitmap maskImage, FileStream fileStreamDifferenceMask)
{
var encodedData = maskImage.Encode(SKEncodedImageFormat.Png, 100);
encodedData.SaveTo(fileStreamDifferenceMask);
Expand All @@ -243,7 +244,7 @@ public void ShouldCalcDiffMaskSKBitmapAndUseOutcome(string pathPic1, string path
using (var fileStreamDifferenceMask = File.Create(differenceMaskPicPath))
using (var maskImage = Compare.CalcDiffMaskImage(absolutePic1, absolutePic2, resizeOption))
{
SaveAsPng(maskImage, fileStreamDifferenceMask);
IntegrationTest.SaveAsPng(maskImage, fileStreamDifferenceMask);
}

using var differenceMaskPic = SKBitmap.Decode(differenceMaskPicPath);
Expand Down Expand Up @@ -318,9 +319,10 @@ public void DiffMaskStreams(string pathPic1, string pathPic2, int expectedMeanEr
Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage");
}

[TestCase(png0Rgba32, png1Rgba32, 0)]
[TestCase(colorShift1, colorShift2, 20)]
public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDifferences(string image1RelativePath, string image2RelativePath, int pixelColorShiftTolerance)
[TestCase(png0Rgba32, png1Rgba32, 0, true)]
[TestCase(colorShift1, colorShift2, 20, false)]
[TestCase(colorShift1, colorShift2, 0, true)]
public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDifferences(string image1RelativePath, string image2RelativePath, int pixelColorShiftTolerance, bool expectIsImageEntirelyBlack)
{
var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath);
var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath);
Expand All @@ -339,7 +341,7 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDiffe
ImageExtensions.SaveAsPng(diffMask2Image, diffMask2Stream);
}

Assert.That(IsImageEntirelyBlack(diffMask2Image), Is.True);
Assert.That(IsImageEntirelyBlack(diffMask2Image), Is.EqualTo(expectIsImageEntirelyBlack));

File.Delete(diffMask1Path);
}
Expand Down Expand Up @@ -373,22 +375,42 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByStream_NoDiffere
File.Delete(diffMask1Path);
}

[TestCase(png0Rgba32, png1Rgba32)]
public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferences(string image1RelativePath, string image2RelativePath)
[TestCase(png0Rgba32, png1Rgba32, png1Rgba32, 0, false)]
[TestCase(colorShift1, colorShift1, colorShift2, 15, true)]
public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferences(string image1RelativePath, string image2RelativePath, string image3RelativePath, int expectedPixelColorShiftTolerance, bool expectToleranceMaskToEntirelyBlack)
{
var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath);
var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath);
var image3Path = Path.Combine(AppContext.BaseDirectory, image3RelativePath);

using var image1 = SKBitmap.Decode(image1Path);
using var image2 = SKBitmap.Decode(image2Path);
using var image3 = SKBitmap.Decode(image3Path);

using var diffMask1Image = Compare.CalcDiffMaskImage(image1, image2);

using var diffMask2Image = Compare.CalcDiffMaskImage(image1, image2, diffMask1Image);
using var diffMask2Image = Compare.CalcDiffMaskImage(image1, image3, diffMask1Image, pixelColorShiftTolerance: expectedPixelColorShiftTolerance);

Assert.That(IsImageEntirelyBlack(diffMask1Image), Is.EqualTo(expectToleranceMaskToEntirelyBlack));
Assert.That(IsImageEntirelyBlack(diffMask2Image), Is.True);
}

[TestCase(png0Rgba32, png0Rgba32, 0)]
[TestCase(png0Rgba32, png0Rgba32, 15)]
[TestCase(colorShift1, colorShift2, 15)]
public void CalcDiffMaskImage_ToleranceColorShift_NoDifferences(string image1RelativePath, string image2RelativePath, int expectedPixelColorShiftTolerance)
{
var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath);
var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath);

using var image1 = SKBitmap.Decode(image1Path);
using var image2 = SKBitmap.Decode(image2Path);

using var diffMask1Image = Compare.CalcDiffMaskImage(image1, image2, pixelColorShiftTolerance: expectedPixelColorShiftTolerance);

Assert.That(IsImageEntirelyBlack(diffMask1Image), Is.True);
}

[Test]
[TestCase(jpg0Rgb24, jpg1Rgb24)]
[TestCase(png0Rgba32, png1Rgba32)]
Expand Down
12 changes: 6 additions & 6 deletions SkiaSharpCompareTestNunit/SkiaSharpCompareTestNunit.csproj
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<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.0">
<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.0.1" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.18.0.83559">
<PackageReference Include="nunit" Version="4.3.2" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.8.0.113526">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.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.8.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading