Esempio n. 1
0
        public void SinglePixelOpaque()
        {
            Configuration config    = Configuration.Default;
            var           quantizer = new WuQuantizer(false);

            using (var image = new Image <Rgba32>(config, 1, 1, Rgba32.Black))
                using (QuantizedFrame <Rgba32> result = quantizer.CreateFrameQuantizer <Rgba32>(config).QuantizeFrame(image.Frames[0]))
                {
                    Assert.Equal(1, result.Palette.Length);
                    Assert.Equal(1, result.GetPixelSpan().Length);

                    Assert.Equal(Rgba32.Black, result.Palette[0]);
                    Assert.Equal(0, result.GetPixelSpan()[0]);
                }
        }
Esempio n. 2
0
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            IFrameQuantizer <TPixel> executor = this.Quantizer.CreateFrameQuantizer <TPixel>();

            using (QuantizedFrame <TPixel> quantized = executor.QuantizeFrame(source))
            {
                int paletteCount = quantized.Palette.Length - 1;

                // Not parallel to remove "quantized" closure allocation.
                // We can operate directly on the source here as we've already read it to get the
                // quantized result
                for (int y = 0; y < source.Height; y++)
                {
                    Span <TPixel>       row = source.GetPixelRowSpan(y);
                    ReadOnlySpan <byte> quantizedPixelSpan = quantized.GetPixelSpan();
                    int yy = y * source.Width;

                    for (int x = 0; x < source.Width; x++)
                    {
                        int i = x + yy;
                        row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])];
                    }
                }
            }
        }
Esempio n. 3
0
        public void SinglePixelOpaque()
        {
            Configuration config    = Configuration.Default;
            var           quantizer = new WuQuantizer(new QuantizerOptions {
                Dither = null
            });

            using var image = new Image <Rgba32>(config, 1, 1, Color.Black);
            ImageFrame <Rgba32> frame = image.Frames.RootFrame;

            using IFrameQuantizer <Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer <Rgba32>(config);
            using QuantizedFrame <Rgba32> result          = frameQuantizer.QuantizeFrame(frame, frame.Bounds());

            Assert.Equal(1, result.Palette.Length);
            Assert.Equal(1, result.GetPixelSpan().Length);

            Assert.Equal(Color.Black, (Color)result.Palette.Span[0]);
            Assert.Equal(0, result.GetPixelSpan()[0]);
        }
Esempio n. 4
0
        public void SinglePixelTransparent()
        {
            Configuration config    = Configuration.Default;
            var           quantizer = new WuQuantizer(false);

            using (var image = new Image <Rgba32>(config, 1, 1, default(Rgba32)))
                using (QuantizedFrame <Rgba32> result = quantizer.CreateFrameQuantizer <Rgba32>(config).QuantizeFrame(image.Frames[0]))
                {
                    Assert.Equal(1, result.Palette.Length);
                    Assert.Equal(1, result.GetPixelSpan().Length);

                    Assert.Equal(default, result.Palette[0]);
Esempio n. 5
0
        /// <inheritdoc/>
        public virtual QuantizedFrame <TPixel> QuantizeFrame(ImageFrame <TPixel> image)
        {
            Guard.NotNull(image, nameof(image));

            // Get the size of the source image
            int height = image.Height;
            int width  = image.Width;

            // Call the FirstPass function if not a single pass algorithm.
            // For something like an Octree quantizer, this will run through
            // all image pixels, build a data structure, and create a palette.
            if (!this.singlePass)
            {
                this.FirstPass(image, width, height);
            }

            // Collect the palette. Required before the second pass runs.
            TPixel[] palette = this.GetPalette();
            this.paletteVector = new Vector4[palette.Length];
            PixelOperations <TPixel> .Instance.ToScaledVector4(palette, this.paletteVector, palette.Length);

            var quantizedFrame = new QuantizedFrame <TPixel>(image.MemoryAllocator, width, height, palette);

            if (this.Dither)
            {
                // We clone the image as we don't want to alter the original via dithering.
                using (ImageFrame <TPixel> clone = image.Clone())
                {
                    this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette, width, height);
                }
            }
            else
            {
                this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette, width, height);
            }

            return(quantizedFrame);
        }
Esempio n. 6
0
        public void WuQuantizerYieldsCorrectTransparentPixel <TPixel>(TestImageProvider <TPixel> provider, bool dither)
            where TPixel : struct, IPixel <TPixel>
        {
            using (Image <TPixel> image = provider.GetImage())
            {
                Assert.True(image[0, 0].Equals(default(TPixel)));

                var quantizer = new WuQuantizer(dither);

                foreach (ImageFrame <TPixel> frame in image.Frames)
                {
                    QuantizedFrame <TPixel> quantized = quantizer.CreateFrameQuantizer <TPixel>().QuantizeFrame(frame);

                    int index = this.GetTransparentIndex(quantized);
                    Assert.Equal(index, quantized.GetPixelSpan()[0]);
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.width  = image.Width;
            this.height = image.Height;

            stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);

            QuantizedFrame <TPixel> quantized           = null;
            ReadOnlySpan <byte>     quantizedPixelsSpan = default;

            if (this.pngColorType == PngColorType.Palette)
            {
                // Create quantized frame returning the palette and set the bit depth.
                quantized           = this.quantizer.CreateFrameQuantizer <TPixel>().QuantizeFrame(image.Frames.RootFrame);
                quantizedPixelsSpan = quantized.GetPixelSpan();
                byte bits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);

                // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
                if (bits == 3)
                {
                    bits = 4;
                }
                else if (bits >= 5 || bits <= 7)
                {
                    bits = 8;
                }

                this.bitDepth = bits;
            }
            else
            {
                this.bitDepth = (byte)(this.use16Bit ? 16 : 8);
            }

            this.bytesPerPixel = this.CalculateBytesPerPixel();

            var header = new PngHeader(
                width: image.Width,
                height: image.Height,
                bitDepth: this.bitDepth,
                colorType: this.pngColorType,
                compressionMethod: 0, // None
                filterMethod: 0,
                interlaceMethod: 0);  // TODO: Can't write interlaced yet.

            this.WriteHeaderChunk(stream, header);

            // Collect the indexed pixel data
            if (quantized != null)
            {
                this.WritePaletteChunk(stream, header, quantized);
            }

            this.WritePhysicalChunk(stream, image);
            this.WriteGammaChunk(stream);
            this.WriteDataChunks(image.Frames.RootFrame, quantizedPixelsSpan, stream);
            this.WriteEndChunk(stream);
            stream.Flush();

            quantized?.Dispose();
        }
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.width  = image.Width;
            this.height = image.Height;

            // Always take the encoder options over the metadata values.
            ImageMetaData metaData    = image.MetaData;
            PngMetaData   pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance);

            this.gamma        = this.gamma ?? pngMetaData.Gamma;
            this.writeGamma   = this.gamma > 0;
            this.pngColorType = this.pngColorType ?? pngMetaData.ColorType;
            this.pngBitDepth  = this.pngBitDepth ?? pngMetaData.BitDepth;
            this.use16Bit     = this.pngBitDepth.Equals(PngBitDepth.Bit16);

            stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);

            QuantizedFrame <TPixel> quantized           = null;
            ReadOnlySpan <byte>     quantizedPixelsSpan = default;

            if (this.pngColorType == PngColorType.Palette)
            {
                byte bits;

                // Use the metadata to determine what quantization depth to use if no quantizer has been set.
                if (this.quantizer == null)
                {
                    bits = (byte)Math.Min(8u, (short)this.pngBitDepth);
                    int colorSize = ImageMaths.GetColorCountForBitDepth(bits);
                    this.quantizer = new WuQuantizer(colorSize);
                }

                // Create quantized frame returning the palette and set the bit depth.
                quantized           = this.quantizer.CreateFrameQuantizer <TPixel>().QuantizeFrame(image.Frames.RootFrame);
                quantizedPixelsSpan = quantized.GetPixelSpan();
                bits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);

                // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
                if (bits == 3)
                {
                    bits = 4;
                }
                else if (bits >= 5 && bits <= 7)
                {
                    bits = 8;
                }

                this.bitDepth = bits;
            }
            else
            {
                this.bitDepth = (byte)(this.use16Bit ? 16 : 8);
            }

            this.bytesPerPixel = this.CalculateBytesPerPixel();

            var header = new PngHeader(
                width: image.Width,
                height: image.Height,
                bitDepth: this.bitDepth,
                colorType: this.pngColorType.Value,
                compressionMethod: 0, // None
                filterMethod: 0,
                interlaceMethod: 0);  // TODO: Can't write interlaced yet.

            this.WriteHeaderChunk(stream, header);

            // Collect the indexed pixel data
            if (quantized != null)
            {
                this.WritePaletteChunk(stream, header, quantized);
            }

            this.WritePhysicalChunk(stream, metaData);
            this.WriteGammaChunk(stream);
            this.WriteExifChunk(stream, metaData);
            this.WriteDataChunks(image.Frames.RootFrame, quantizedPixelsSpan, stream);
            this.WriteEndChunk(stream);
            stream.Flush();

            quantized?.Dispose();
        }