// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Tests; public class QuantizedImageTests { private Configuration Configuration => Configuration.Default; [Fact] public void QuantizersDitherByDefault() { WernerPaletteQuantizer werner = new(); WebSafePaletteQuantizer webSafe = new(); OctreeQuantizer octree = new(); WuQuantizer wu = new(); Assert.NotNull(werner.Options.Dither); Assert.NotNull(webSafe.Options.Dither); Assert.NotNull(octree.Options.Dither); Assert.NotNull(wu.Options.Dither); using (IQuantizer quantizer = werner.CreatePixelSpecificQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } using (IQuantizer quantizer = webSafe.CreatePixelSpecificQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } using (IQuantizer quantizer = octree.CreatePixelSpecificQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } using (IQuantizer quantizer = wu.CreatePixelSpecificQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } } [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] public void OctreeQuantizerYieldsCorrectTransparentPixel( TestImageProvider provider, bool dither) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); Assert.True(image[0, 0].Equals(default)); QuantizerOptions options = new(); if (!dither) { options.Dither = null; } OctreeQuantizer quantizer = new(options); foreach (ImageFrame frame in image.Frames) { using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); int index = this.GetTransparentIndex(quantized); Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]); } } [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] public void WuQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); Assert.True(image[0, 0].Equals(default)); QuantizerOptions options = new(); if (!dither) { options.Dither = null; } WuQuantizer quantizer = new(options); foreach (ImageFrame frame in image.Frames) { using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); int index = this.GetTransparentIndex(quantized); Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]); } } // Test case for issue: https://github.com/SixLabors/ImageSharp/issues/1505 [Theory] [WithFile(TestImages.Gif.Issues.Issue1505, PixelTypes.Rgba32)] public void Issue1505(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); OctreeQuantizer octreeQuantizer = new(); IQuantizer quantizer = octreeQuantizer.CreatePixelSpecificQuantizer(Configuration.Default, new QuantizerOptions() { MaxColors = 128 }); ImageFrame frame = image.Frames[0]; quantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); } private int GetTransparentIndex(IndexedImageFrame quantized) where TPixel : unmanaged, IPixel { // Transparent pixels are much more likely to be found at the end of a palette int index = -1; ReadOnlySpan paletteSpan = quantized.Palette.Span; Span colorSpan = stackalloc Rgba32[QuantizerConstants.MaxColors].Slice(0, paletteSpan.Length); PixelOperations.Instance.ToRgba32(quantized.Configuration, paletteSpan, colorSpan); for (int i = colorSpan.Length - 1; i >= 0; i--) { if (colorSpan[i].Equals(default)) { index = i; } } return index; } }