// Copyright (c) Six Labors and contributors. // Licensed under the GNU Affero General Public License, Version 3. using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; using Xunit; namespace SixLabors.ImageSharp.Tests { public class QuantizedImageTests { private Configuration Configuration => Configuration.Default; [Fact] public void QuantizersDitherByDefault() { var werner = new WernerPaletteQuantizer(); var webSafe = new WebSafePaletteQuantizer(); var octree = new OctreeQuantizer(); var wu = new WuQuantizer(); 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)); var options = new QuantizerOptions(); if (!dither) { options.Dither = null; } var quantizer = new OctreeQuantizer(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.GetPixelRowSpan(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)); var options = new QuantizerOptions(); if (!dither) { options.Dither = null; } var quantizer = new WuQuantizer(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.GetPixelRowSpan(0)[0]); } } } } 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; } } }