// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. 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 (IFrameQuantizer quantizer = werner.CreateFrameQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } using (IFrameQuantizer quantizer = webSafe.CreateFrameQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } using (IFrameQuantizer quantizer = octree.CreateFrameQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } using (IFrameQuantizer quantizer = wu.CreateFrameQuantizer(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 (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(this.Configuration)) using (IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(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 (IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(this.Configuration)) using (IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(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; } } }