// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Linq;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
{
public abstract class ImageComparer
{
public static ImageComparer Exact { get; } = Tolerant(0, 0);
///
/// Returns an instance of .
/// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'.
///
public static ImageComparer Tolerant(
float imageThreshold = TolerantImageComparer.DefaultImageThreshold,
int perPixelManhattanThreshold = 0)
{
return new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold);
}
///
/// Returns Tolerant(imageThresholdInPercents/100)
///
public static ImageComparer TolerantPercentage(float imageThresholdInPercents, int perPixelManhattanThreshold = 0)
=> Tolerant(imageThresholdInPercents / 100F, perPixelManhattanThreshold);
public abstract ImageSimilarityReport CompareImagesOrFrames(
ImageFrame expected,
ImageFrame actual)
where TPixelA : struct, IPixel where TPixelB : struct, IPixel;
}
public static class ImageComparerExtensions
{
public static ImageSimilarityReport CompareImagesOrFrames(
this ImageComparer comparer,
Image expected,
Image actual)
where TPixelA : struct, IPixel where TPixelB : struct, IPixel
{
return comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame);
}
public static IEnumerable> CompareImages(
this ImageComparer comparer,
Image expected,
Image actual)
where TPixelA : struct, IPixel where TPixelB : struct, IPixel
{
var result = new List>();
if (expected.Frames.Count != actual.Frames.Count)
{
throw new Exception("Frame count does not match!");
}
for (int i = 0; i < expected.Frames.Count; i++)
{
ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]);
if (!report.IsEmpty)
{
result.Add(report);
}
}
return result;
}
public static void VerifySimilarity(
this ImageComparer comparer,
Image expected,
Image actual)
where TPixelA : struct, IPixel where TPixelB : struct, IPixel
{
if (expected.Size() != actual.Size())
{
throw new ImageDimensionsMismatchException(expected.Size(), actual.Size());
}
if (expected.Frames.Count != actual.Frames.Count)
{
throw new ImagesSimilarityException("Image frame count does not match!");
}
IEnumerable reports = comparer.CompareImages(expected, actual);
if (reports.Any())
{
throw new ImageDifferenceIsOverThresholdException(reports);
}
}
public static void VerifySimilarityIgnoreRegion(
this ImageComparer comparer,
Image expected,
Image actual,
Rectangle ignoredRegion)
where TPixelA : struct, IPixel
where TPixelB : struct, IPixel
{
if (expected.Size() != actual.Size())
{
throw new ImageDimensionsMismatchException(expected.Size(), actual.Size());
}
if (expected.Frames.Count != actual.Frames.Count)
{
throw new ImagesSimilarityException("Image frame count does not match!");
}
IEnumerable> reports = comparer.CompareImages(expected, actual);
if (reports.Any())
{
var cleanedReports = new List>(reports.Count());
foreach (ImageSimilarityReport r in reports)
{
IEnumerable outsideChanges = r.Differences.Where(
x =>
!(ignoredRegion.X <= x.Position.X
&& x.Position.X <= ignoredRegion.Right
&& ignoredRegion.Y <= x.Position.Y
&& x.Position.Y <= ignoredRegion.Bottom));
if (outsideChanges.Any())
{
cleanedReports.Add(new ImageSimilarityReport(r.ExpectedImage, r.ActualImage, outsideChanges, null));
}
}
if (cleanedReports.Count > 0)
{
throw new ImageDifferenceIsOverThresholdException(cleanedReports);
}
}
}
}
}