// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization
{
public class QuantizerTests
{
///
/// Something is causing tests to fail on NETFX in CI.
/// Could be a JIT error as everything runs well and is identical to .NET Core output.
/// Not worth investigating for now.
///
///
private static readonly bool SkipAllQuantizerTests = TestEnvironment.IsFramework;
public static readonly string[] CommonTestImages =
{
TestImages.Png.CalliphoraPartial,
TestImages.Png.Bike
};
private static readonly QuantizerOptions NoDitherOptions = new QuantizerOptions { Dither = null };
private static readonly QuantizerOptions DiffuserDitherOptions = new QuantizerOptions { Dither = KnownDitherings.FloydSteinberg };
private static readonly QuantizerOptions OrderedDitherOptions = new QuantizerOptions { Dither = KnownDitherings.Bayer8x8 };
private static readonly QuantizerOptions Diffuser0_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = 0F
};
private static readonly QuantizerOptions Diffuser0_25_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = .25F
};
private static readonly QuantizerOptions Diffuser0_5_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = .5F
};
private static readonly QuantizerOptions Diffuser0_75_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = .75F
};
private static readonly QuantizerOptions Ordered0_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = 0F
};
private static readonly QuantizerOptions Ordered0_25_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = .25F
};
private static readonly QuantizerOptions Ordered0_5_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = .5F
};
private static readonly QuantizerOptions Ordered0_75_ScaleDitherOptions = new QuantizerOptions
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = .75F
};
public static readonly TheoryData Quantizers
= new TheoryData
{
// Known uses error diffusion by default.
KnownQuantizers.Octree,
KnownQuantizers.WebSafe,
KnownQuantizers.Werner,
KnownQuantizers.Wu,
new OctreeQuantizer(NoDitherOptions),
new WebSafePaletteQuantizer(NoDitherOptions),
new WernerPaletteQuantizer(NoDitherOptions),
new WuQuantizer(NoDitherOptions),
new OctreeQuantizer(OrderedDitherOptions),
new WebSafePaletteQuantizer(OrderedDitherOptions),
new WernerPaletteQuantizer(OrderedDitherOptions),
new WuQuantizer(OrderedDitherOptions)
};
public static readonly TheoryData DitherScaleQuantizers
= new TheoryData
{
new OctreeQuantizer(Diffuser0_ScaleDitherOptions),
new WebSafePaletteQuantizer(Diffuser0_ScaleDitherOptions),
new WernerPaletteQuantizer(Diffuser0_ScaleDitherOptions),
new WuQuantizer(Diffuser0_ScaleDitherOptions),
new OctreeQuantizer(Diffuser0_25_ScaleDitherOptions),
new WebSafePaletteQuantizer(Diffuser0_25_ScaleDitherOptions),
new WernerPaletteQuantizer(Diffuser0_25_ScaleDitherOptions),
new WuQuantizer(Diffuser0_25_ScaleDitherOptions),
new OctreeQuantizer(Diffuser0_5_ScaleDitherOptions),
new WebSafePaletteQuantizer(Diffuser0_5_ScaleDitherOptions),
new WernerPaletteQuantizer(Diffuser0_5_ScaleDitherOptions),
new WuQuantizer(Diffuser0_5_ScaleDitherOptions),
new OctreeQuantizer(Diffuser0_75_ScaleDitherOptions),
new WebSafePaletteQuantizer(Diffuser0_75_ScaleDitherOptions),
new WernerPaletteQuantizer(Diffuser0_75_ScaleDitherOptions),
new WuQuantizer(Diffuser0_75_ScaleDitherOptions),
new OctreeQuantizer(DiffuserDitherOptions),
new WebSafePaletteQuantizer(DiffuserDitherOptions),
new WernerPaletteQuantizer(DiffuserDitherOptions),
new WuQuantizer(DiffuserDitherOptions),
new OctreeQuantizer(Ordered0_ScaleDitherOptions),
new WebSafePaletteQuantizer(Ordered0_ScaleDitherOptions),
new WernerPaletteQuantizer(Ordered0_ScaleDitherOptions),
new WuQuantizer(Ordered0_ScaleDitherOptions),
new OctreeQuantizer(Ordered0_25_ScaleDitherOptions),
new WebSafePaletteQuantizer(Ordered0_25_ScaleDitherOptions),
new WernerPaletteQuantizer(Ordered0_25_ScaleDitherOptions),
new WuQuantizer(Ordered0_25_ScaleDitherOptions),
new OctreeQuantizer(Ordered0_5_ScaleDitherOptions),
new WebSafePaletteQuantizer(Ordered0_5_ScaleDitherOptions),
new WernerPaletteQuantizer(Ordered0_5_ScaleDitherOptions),
new WuQuantizer(Ordered0_5_ScaleDitherOptions),
new OctreeQuantizer(Ordered0_75_ScaleDitherOptions),
new WebSafePaletteQuantizer(Ordered0_75_ScaleDitherOptions),
new WernerPaletteQuantizer(Ordered0_75_ScaleDitherOptions),
new WuQuantizer(Ordered0_75_ScaleDitherOptions),
new OctreeQuantizer(OrderedDitherOptions),
new WebSafePaletteQuantizer(OrderedDitherOptions),
new WernerPaletteQuantizer(OrderedDitherOptions),
new WuQuantizer(OrderedDitherOptions),
};
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F);
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(Quantizers), PixelTypes.Rgba32)]
public void ApplyQuantizationInBox(TestImageProvider provider, IQuantizer quantizer)
where TPixel : unmanaged, IPixel
{
if (SkipAllQuantizerTests)
{
return;
}
string quantizerName = quantizer.GetType().Name;
string ditherName = quantizer.Options.Dither?.GetType()?.Name ?? "NoDither";
string testOutputDetails = $"{quantizerName}_{ditherName}";
provider.RunRectangleConstrainedValidatingProcessorTest(
(x, rect) => x.Quantize(quantizer, rect),
testOutputDetails: testOutputDetails,
comparer: ValidatorComparer,
appendPixelTypeToFileName: false);
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(Quantizers), PixelTypes.Rgba32)]
public void ApplyQuantization(TestImageProvider provider, IQuantizer quantizer)
where TPixel : unmanaged, IPixel
{
if (SkipAllQuantizerTests)
{
return;
}
string quantizerName = quantizer.GetType().Name;
string ditherName = quantizer.Options.Dither?.GetType()?.Name ?? "NoDither";
string testOutputDetails = $"{quantizerName}_{ditherName}";
provider.RunValidatingProcessorTest(
x => x.Quantize(quantizer),
comparer: ValidatorComparer,
testOutputDetails: testOutputDetails,
appendPixelTypeToFileName: false);
}
[Theory]
[WithFile(TestImages.Png.David, nameof(DitherScaleQuantizers), PixelTypes.Rgba32)]
public void ApplyQuantizationWithDitheringScale(TestImageProvider provider, IQuantizer quantizer)
where TPixel : unmanaged, IPixel
{
if (SkipAllQuantizerTests)
{
return;
}
string quantizerName = quantizer.GetType().Name;
string ditherName = quantizer.Options.Dither.GetType().Name;
float ditherScale = quantizer.Options.DitherScale;
string testOutputDetails = FormattableString.Invariant($"{quantizerName}_{ditherName}_{ditherScale}");
provider.RunValidatingProcessorTest(
x => x.Quantize(quantizer),
comparer: ValidatorComparer,
testOutputDetails: testOutputDetails,
appendPixelTypeToFileName: false);
}
}
}