Exemple #1
0
        /// <summary>
        /// Applies quantization to the image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="source">The image this method extends.</param>
        /// <param name="quantizer">The quantizer to apply to perform the operation.</param>
        /// <param name="maxColors">The maximum number of colors to return.</param>
        /// <returns>The <see cref="Image{TPixel}"/>.</returns>
        public static IImageProcessingContext <TPixel> Quantize <TPixel>(this IImageProcessingContext <TPixel> source, IQuantizer <TPixel> quantizer, int maxColors)
            where TPixel : struct, IPixel <TPixel>
        {
            return(source.Apply(img =>
            {
                // TODO : move helper logic into the processor
                QuantizedImage <TPixel> quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors);
                int palleteCount = quantized.Palette.Length - 1;

                using (Buffer2D <TPixel> pixels = source.MemoryManager.Allocate2D <TPixel>(quantized.Width, quantized.Height))
                {
                    Parallel.For(
                        0,
                        pixels.Height,
                        img.GetConfiguration().ParallelOptions,
                        y =>
                    {
                        Span <TPixel> row = pixels.GetRowSpan(y);
                        int yy = y * pixels.Width;
                        for (int x = 0; x < pixels.Width; x++)
                        {
                            int i = x + yy;
                            TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])];
                            row[x] = color;
                        }
                    });

                    Buffer2D <TPixel> .SwapContents(img.Frames[0].PixelBuffer, pixels);
                }
            }));
        }
Exemple #2
0
        /// <summary>
        /// Writes the color table to the stream.
        /// </summary>
        /// <typeparam name="T">The pixel format.</typeparam>
        /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
        /// <param name="image">The <see cref="ImageBase{T,TP}"/> to encode.</param>
        /// <param name="writer">The writer to write to the stream with.</param>
        private void WriteColorTable <T, TP>(QuantizedImage <T, TP> image, EndianBinaryWriter writer)
            where T : IPackedVector <TP>
            where TP : struct
        {
            // Grab the palette and write it to the stream.
            T[] palette    = image.Palette;
            int pixelCount = palette.Length;

            // Get max colors for bit depth.
            int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;

            byte[] colorTable = new byte[colorTableLength];

            Parallel.For(0, pixelCount,
                         i =>
            {
                int offset   = i * 3;
                byte[] color = palette[i].ToBytes();

                colorTable[offset]     = color[0];
                colorTable[offset + 1] = color[1];
                colorTable[offset + 2] = color[2];
            });

            writer.Write(colorTable, 0, colorTableLength);
        }
Exemple #3
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TColor}"/>.
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <param name="image">The <see cref="Image{TColor}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TColor>(Image <TColor> image, Stream stream)
            where TColor : struct, IPackedPixel, IEquatable <TColor>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            if (this.Quantizer == null)
            {
                this.Quantizer = new OctreeQuantizer <TColor>();
            }

            // Do not use IDisposable pattern here as we want to preserve the stream.
            EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);

            // Ensure that quality can be set but has a fallback.
            int quality = this.Quality > 0 ? this.Quality : image.MetaData.Quality;

            this.Quality = quality > 0 ? quality.Clamp(1, 256) : 256;

            // Get the number of bits.
            this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality);

            // Quantize the image returning a palette.
            QuantizedImage <TColor> quantized = ((IQuantizer <TColor>) this.Quantizer).Quantize(image, this.Quality);

            int index = this.GetTransparentIndex(quantized);

            // Write the header.
            this.WriteHeader(writer);

            // Write the LSD. We'll use local color tables for now.
            this.WriteLogicalScreenDescriptor(image, writer, index);

            // Write the first frame.
            this.WriteGraphicalControlExtension(image, writer, index);
            this.WriteImageDescriptor(image, writer);
            this.WriteColorTable(quantized, writer);
            this.WriteImageData(quantized, writer);

            // Write additional frames.
            if (image.Frames.Any())
            {
                this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count);

                // ReSharper disable once ForCanBeConvertedToForeach
                for (int i = 0; i < image.Frames.Count; i++)
                {
                    ImageFrame <TColor>     frame          = image.Frames[i];
                    QuantizedImage <TColor> quantizedFrame = ((IQuantizer <TColor>) this.Quantizer).Quantize(frame, this.Quality);

                    this.WriteGraphicalControlExtension(frame, writer, this.GetTransparentIndex(quantizedFrame));
                    this.WriteImageDescriptor(frame, writer);
                    this.WriteColorTable(quantizedFrame, writer);
                    this.WriteImageData(quantizedFrame, writer);
                }
            }

            // TODO: Write Comments extension etc
            writer.Write(GifConstants.EndIntroducer);
        }
Exemple #4
0
        /// <summary>
        /// Writes the color table to the stream.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
        /// <param name="writer">The writer to write to the stream with.</param>
        private void WriteColorTable <TPixel>(QuantizedImage <TPixel> image, EndianBinaryWriter writer)
            where TPixel : struct, IPixel <TPixel>
        {
            // Grab the palette and write it to the stream.
            int pixelCount = image.Palette.Length;

            // Get max colors for bit depth.
            int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;
            var rgb = default(Rgb24);

            using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
            {
                Span <byte> colorTableSpan = colorTable.Span;

                for (int i = 0; i < pixelCount; i++)
                {
                    int offset = i * 3;
                    image.Palette[i].ToRgb24(ref rgb);
                    colorTableSpan[offset]     = rgb.R;
                    colorTableSpan[offset + 1] = rgb.G;
                    colorTableSpan[offset + 2] = rgb.B;
                }

                writer.Write(colorTable.Array, 0, colorTableLength);
            }
        }
Exemple #5
0
        /// <summary>
        /// Writes the color table to the stream.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode.</param>
        /// <param name="writer">The writer to write to the stream with.</param>
        private void WriteColorTable <TPixel>(QuantizedImage <TPixel> image, EndianBinaryWriter writer)
            where TPixel : struct, IPixel <TPixel>
        {
            // Grab the palette and write it to the stream.
            int pixelCount = image.Palette.Length;

            // Get max colors for bit depth.
            int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;

            byte[] colorTable = ArrayPool <byte> .Shared.Rent(colorTableLength);

            try
            {
                for (int i = 0; i < pixelCount; i++)
                {
                    int offset = i * 3;
                    image.Palette[i].ToXyzBytes(this.buffer, 0);
                    colorTable[offset]     = this.buffer[0];
                    colorTable[offset + 1] = this.buffer[1];
                    colorTable[offset + 2] = this.buffer[2];
                }

                writer.Write(colorTable, 0, colorTableLength);
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(colorTable);
            }
        }
Exemple #6
0
        /// <summary>
        /// Writes the color table to the stream.
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
        /// <param name="image">The <see cref="ImageBase{TColor, TPacked}"/> to encode.</param>
        /// <param name="writer">The writer to write to the stream with.</param>
        private void WriteColorTable <TColor, TPacked>(QuantizedImage <TColor, TPacked> image, EndianBinaryWriter writer)
            where TColor : struct, IPackedPixel <TPacked>
            where TPacked : struct
        {
            // Grab the palette and write it to the stream.
            TColor[] palette    = image.Palette;
            int      pixelCount = palette.Length;

            // Get max colors for bit depth.
            int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;

            byte[] colorTable = new byte[colorTableLength];

            Parallel.For(
                0,
                pixelCount,
                i =>
            {
                int offset  = i * 3;
                Color color = new Color(palette[i].ToVector4());

                colorTable[offset]     = color.R;
                colorTable[offset + 1] = color.G;
                colorTable[offset + 2] = color.B;
            });

            writer.Write(colorTable, 0, colorTableLength);
        }
        private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer)
        {
            byte[]     indexedPixels = image.Pixels;
            LzwEncoder encoder       = new LzwEncoder(indexedPixels, (byte)bitDepth);

            encoder.Encode(writer.BaseStream);
        }
Exemple #8
0
        /// <summary>
        /// Applies quantization to the image.
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <param name="source">The image this method extends.</param>
        /// <param name="quantizer">The quantizer to apply to perform the operation.</param>
        /// <param name="maxColors">The maximum number of colors to return.</param>
        /// <returns>The <see cref="Image{TColor}"/>.</returns>
        public static Image <TColor> Quantize <TColor>(this Image <TColor> source, IQuantizer <TColor> quantizer, int maxColors)
            where TColor : struct, IPackedPixel, IEquatable <TColor>
        {
            QuantizedImage <TColor> quantized = quantizer.Quantize(source, maxColors);

            int pixelCount   = quantized.Pixels.Length;
            int palleteCount = quantized.Palette.Length - 1;

            using (PixelAccessor <TColor> pixels = new PixelAccessor <TColor>(quantized.Width, quantized.Height))
            {
                Parallel.For(
                    0,
                    pixels.Height,
                    source.Configuration.ParallelOptions,
                    y =>
                {
                    for (var x = 0; x < pixels.Width; x++)
                    {
                        var i        = x + (y * pixels.Width);
                        TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])];
                        pixels[x, y] = color;
                    }
                });

                source.SwapPixelsBuffers(pixels);
                return(source);
            }
        }
Exemple #9
0
        public static Image2 Quantize(this Image2 source, IQuantizer quantizer, int maxColors)
        {
            QuantizedImage quantizedImage = quantizer.Quantize(source, maxColors);

            source.SetPixels(source.Width, source.Height, quantizedImage.ToImage().Pixels);
            return(source);
        }
Exemple #10
0
        /// <summary>
        /// Applies quantization to the image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="source">The image this method extends.</param>
        /// <param name="quantizer">The quantizer to apply to perform the operation.</param>
        /// <param name="maxColors">The maximum number of colors to return.</param>
        /// <returns>The <see cref="Image{TPixel}"/>.</returns>
        public static IImageProcessingContext <TPixel> Quantize <TPixel>(this IImageProcessingContext <TPixel> source, IQuantizer <TPixel> quantizer, int maxColors)
            where TPixel : struct, IPixel <TPixel>
        {
            return(source.Apply(img =>
            {
                // TODO : move helper logic into the processor
                QuantizedImage <TPixel> quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors);
                int palleteCount = quantized.Palette.Length - 1;

                using (var pixels = new PixelAccessor <TPixel>(quantized.Width, quantized.Height))
                {
                    Parallel.For(
                        0,
                        pixels.Height,
                        img.GetConfiguration().ParallelOptions,
                        y =>
                    {
                        for (int x = 0; x < pixels.Width; x++)
                        {
                            int i = x + (y * pixels.Width);
                            TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])];
                            pixels[x, y] = color;
                        }
                    });

                    img.Frames[0].SwapPixelsBuffers(pixels);
                }
            }));
        }
        private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image)
        {
            if (Quality > 256)
            {
                return(null);
            }
            if (Quantizer == null)
            {
                Quantizer = new WuQuantizer {
                    Threshold = Threshold
                };
            }
            QuantizedImage quantized = ((IQuantizer)Quantizer).Quantize(image, Quality);

            Color2[] palette          = quantized.Palette;
            int      pixelCount       = palette.Length;
            int      colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3;

            byte[] colorTable = new byte[colorTableLength];
            Parallel.For(0, pixelCount, Bootstrapper.instance.ParallelOptions, i => {
                int offset             = i * 3;
                byte[] color           = palette[i].ToBytes();
                colorTable[offset]     = color[0];
                colorTable[offset + 1] = color[1];
                colorTable[offset + 2] = color[2];
            });
            WriteChunk(stream, PngChunkTypes.Palette, colorTable);
            if (quantized.TransparentIndex > -1)
            {
                WriteChunk(stream, PngChunkTypes.PaletteAlpha, new[] { (byte)quantized.TransparentIndex });
            }
            return(quantized);
        }
Exemple #12
0
        /// <summary>
        /// Returns the index of the most transparent color in the palette.
        /// </summary>
        /// <param name="quantized">
        /// The quantized.
        /// </param>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        private int GetTransparentIndex <TPixel>(QuantizedImage <TPixel> quantized)
            where TPixel : struct, IPixel <TPixel>
        {
            // Find the lowest alpha value and make it the transparent index.
            int  index    = 255;
            byte alpha    = 255;
            bool hasEmpty = false;

            // Some images may have more than one quantized pixel returned with an alpha value of zero
            // so we should always ignore if we have empty pixels present.
            for (int i = 0; i < quantized.Palette.Length; i++)
            {
                quantized.Palette[i].ToXyzwBytes(this.buffer, 0);

                if (!hasEmpty)
                {
                    if (this.buffer[0] == 0 && this.buffer[1] == 0 && this.buffer[2] == 0 && this.buffer[3] == 0)
                    {
                        alpha    = this.buffer[3];
                        index    = i;
                        hasEmpty = true;
                    }

                    if (this.buffer[3] < alpha)
                    {
                        alpha = this.buffer[3];
                        index = i;
                    }
                }
            }

            return(index);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="Frame"/> class.
 /// </summary>
 /// <param name="image">The image.</param>
 /// <param name="quantizedImage">The quantized image.</param>
 /// <param name="bitDepth">The bit depth.</param>
 /// <param name="delay">The delay.</param>
 public Frame(Image image, QuantizedImage quantizedImage, int bitDepth, short delay)
     : this(new GraphicsControl(image, quantizedImage, delay),
            new ImageDescriptor(image, bitDepth),
            new ColorTable(quantizedImage, bitDepth),
            new FrameIndices(quantizedImage, bitDepth),
            null)
 {
 }
Exemple #14
0
 /// <summary>
 /// Writes the image pixel data to the stream.
 /// </summary>
 /// <typeparam name="TPixel">The pixel format.</typeparam>
 /// <param name="image">The <see cref="QuantizedImage{TPixel}"/> containing indexed pixels.</param>
 /// <param name="writer">The stream to write to.</param>
 private void WriteImageData <TPixel>(QuantizedImage <TPixel> image, EndianBinaryWriter writer)
     where TPixel : struct, IPixel <TPixel>
 {
     using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth))
     {
         encoder.Encode(writer.BaseStream);
     }
 }
Exemple #15
0
 /// <summary>
 /// Writes the image pixel data to the stream.
 /// </summary>
 /// <typeparam name="TColor">The pixel format.</typeparam>
 /// <param name="image">The <see cref="QuantizedImage{TColor}"/> containing indexed pixels.</param>
 /// <param name="writer">The stream to write to.</param>
 private void WriteImageData <TColor>(QuantizedImage <TColor> image, EndianBinaryWriter writer)
     where TColor : struct, IPackedPixel, IEquatable <TColor>
 {
     using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth))
     {
         encoder.Encode(writer.BaseStream);
     }
 }
Exemple #16
0
        /// <summary>
        /// Collects the indexed pixel data.
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <param name="image">The image to encode.</param>
        /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
        /// <param name="header">The <see cref="PngHeader"/>.</param>
        private void CollectIndexedBytes <TColor>(ImageBase <TColor> image, Stream stream, PngHeader header)
            where TColor : struct, IPackedPixel, IEquatable <TColor>
        {
            // Quantize the image and get the pixels.
            QuantizedImage <TColor> quantized = this.WritePaletteChunk(stream, header, image);

            this.palettePixelData = quantized.Pixels;
        }
Exemple #17
0
        /// <summary>
        /// Collects the indexed pixel data.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The image to encode.</param>
        /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
        /// <param name="header">The <see cref="PngHeader"/>.</param>
        private void CollectIndexedBytes <TPixel>(ImageFrame <TPixel> image, Stream stream, PngHeader header)
            where TPixel : struct, IPixel <TPixel>
        {
            // Quantize the image and get the pixels.
            QuantizedImage <TPixel> quantized = this.WritePaletteChunk(stream, header, image);

            this.palettePixelData = quantized.Pixels;
        }
        public void Encode(ImageBase imageBase, Stream stream)
        {
            if (imageBase == null || stream == null)
            {
                throw new ArgumentNullException();
            }
            Image2 image = (Image2)imageBase;

            if (Quantizer == null)
            {
                Quantizer = new OctreeQuantizer {
                    Threshold = Threshold
                };
            }

            // Do not use IDisposable pattern here as we want to preserve the stream.
            EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Little, stream);

            // Ensure that quality can be set but has a fallback.
            int quality = Quality > 0 ? Quality : imageBase.Quality;

            Quality = quality > 0 ? NumUtils.Clamp(quality, 1, 256) : 256;

            // Get the number of bits.
            bitDepth = GetBitsNeededForColorDepth(Quality);

            // Quantize the image returning a palette.
            QuantizedImage quantized = Quantizer.Quantize(image, Quality);

            // Write the header.
            WriteHeader(writer);

            // Write the LSD. We'll use local color tables for now.
            WriteLogicalScreenDescriptor(image, writer, quantized.TransparentIndex);

            // Write the first frame.
            WriteGraphicalControlExtension(imageBase, writer, quantized.TransparentIndex);
            WriteImageDescriptor(image, writer);
            WriteColorTable(quantized, writer);
            WriteImageData(quantized, writer);

            // Write additional frames.
            if (image.Frames.Any())
            {
                WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count);
                foreach (ImageFrame frame in image.Frames)
                {
                    QuantizedImage quantizedFrame = Quantizer.Quantize(frame, Quality);
                    WriteGraphicalControlExtension(frame, writer, quantizedFrame.TransparentIndex);
                    WriteImageDescriptor(frame, writer);
                    WriteColorTable(quantizedFrame, writer);
                    WriteImageData(quantizedFrame, writer);
                }
            }

            // TODO: Write Comments extension etc
            writer.Write(GifConstants.endIntroducer);
        }
Exemple #19
0
        /// <summary>
        /// Applies quantization to the image.
        /// </summary>
        /// <typeparam name="T">The pixel format.</typeparam>
        /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
        /// <param name="source">The image this method extends.</param>
        /// <param name="quantizer">The quantizer to apply to perform the operation.</param>
        /// <param name="maxColors">The maximum number of colors to return.</param>
        /// <returns>The <see cref="Image{T,TP}"/>.</returns>
        public static Image <T, TP> Quantize <T, TP>(this Image <T, TP> source, IQuantizer <T, TP> quantizer, int maxColors)
            where T : IPackedVector <TP>
            where TP : struct
        {
            QuantizedImage <T, TP> quantizedImage = quantizer.Quantize(source, maxColors);

            source.SetPixels(source.Width, source.Height, quantizedImage.ToImage().Pixels);
            return(source);
        }
Exemple #20
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{T,TP}"/>.
        /// </summary>
        /// <typeparam name="T">The pixel format.</typeparam>
        /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
        /// <param name="image">The <see cref="Image{T,TP}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <T, TP>(Image <T, TP> image, Stream stream)
            where T : IPackedVector <TP>
            where TP : struct
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            if (this.Quantizer == null)
            {
                this.Quantizer = new OctreeQuantizer <T, TP> {
                    Threshold = this.Threshold
                };
            }

            // Do not use IDisposable pattern here as we want to preserve the stream.
            EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Little, stream);

            // Ensure that quality can be set but has a fallback.
            int quality = this.Quality > 0 ? this.Quality : image.Quality;

            this.Quality = quality > 0 ? quality.Clamp(1, 256) : 256;

            // Get the number of bits.
            this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality);

            // Quantize the image returning a palette.
            QuantizedImage <T, TP> quantized = ((IQuantizer <T, TP>) this.Quantizer).Quantize(image, this.Quality);

            // Write the header.
            this.WriteHeader(writer);

            // Write the LSD. We'll use local color tables for now.
            this.WriteLogicalScreenDescriptor(image, writer, quantized.TransparentIndex);

            // Write the first frame.
            this.WriteGraphicalControlExtension(image, writer, quantized.TransparentIndex);
            this.WriteImageDescriptor(image, writer);
            this.WriteColorTable(quantized, writer);
            this.WriteImageData(quantized, writer);

            // Write additional frames.
            if (image.Frames.Any())
            {
                this.WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count);
                foreach (ImageFrame <T, TP> frame in image.Frames)
                {
                    QuantizedImage <T, TP> quantizedFrame = ((IQuantizer <T, TP>) this.Quantizer).Quantize(frame, this.Quality);
                    this.WriteGraphicalControlExtension(frame, writer, quantizedFrame.TransparentIndex);
                    this.WriteImageDescriptor(frame, writer);
                    this.WriteColorTable(quantizedFrame, writer);
                    this.WriteImageData(quantizedFrame, writer);
                }
            }

            // TODO: Write Comments extension etc
            writer.Write(GifConstants.EndIntroducer);
        }
Exemple #21
0
        /// <summary>
        /// Collects the indexed pixel data.
        /// </summary>
        /// <typeparam name="T">The pixel format.</typeparam>
        /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
        /// <param name="image">The image to encode.</param>
        /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
        /// <param name="header">The <see cref="PngHeader"/>.</param>
        private void CollectIndexedBytes <T, TP>(ImageBase <T, TP> image, Stream stream, PngHeader header)
            where T : IPackedVector <TP>
            where TP : struct
        {
            // Quatize the image and get the pixels
            QuantizedImage <T, TP> quantized = this.WritePaletteChunk(stream, header, image);

            pixelData = quantized.Pixels;
        }
Exemple #22
0
        /// <summary>
        /// Applies quantization to the image.
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
        /// <param name="source">The image this method extends.</param>
        /// <param name="quantizer">The quantizer to apply to perform the operation.</param>
        /// <param name="maxColors">The maximum number of colors to return.</param>
        /// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
        public static Image <TColor, TPacked> Quantize <TColor, TPacked>(this Image <TColor, TPacked> source, IQuantizer <TColor, TPacked> quantizer, int maxColors)
            where TColor : struct, IPackedPixel <TPacked>
            where TPacked : struct, IEquatable <TPacked>
        {
            QuantizedImage <TColor, TPacked> quantizedImage = quantizer.Quantize(source, maxColors);

            source.SetPixels(source.Width, source.Height, quantizedImage.ToImage().Pixels);
            return(source);
        }
Exemple #23
0
        /// <summary>
        /// Writes the image pixel data to the stream.
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
        /// <param name="image">The <see cref="QuantizedImage{TColor, TPacked}"/> containing indexed pixels.</param>
        /// <param name="writer">The stream to write to.</param>
        private void WriteImageData <TColor, TPacked>(QuantizedImage <TColor, TPacked> image, EndianBinaryWriter writer)
            where TColor : struct, IPackedPixel <TPacked>
            where TPacked : struct
        {
            byte[] indexedPixels = image.Pixels;

            LzwEncoder encoder = new LzwEncoder(indexedPixels, (byte)this.bitDepth);

            encoder.Encode(writer.BaseStream);
        }
Exemple #24
0
        /// <summary>
        /// Writes the palette chunk to the stream.
        /// </summary>
        /// <typeparam name="T">The pixel format.</typeparam>
        /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
        /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
        /// <param name="header">The <see cref="PngHeader"/>.</param>
        /// <param name="image">The image to encode.</param>
        private QuantizedImage <T, TP> WritePaletteChunk <T, TP>(Stream stream, PngHeader header, ImageBase <T, TP> image)
            where T : IPackedVector <TP>
            where TP : struct
        {
            if (this.Quality > 256)
            {
                return(null);
            }

            if (this.Quantizer == null)
            {
                this.Quantizer = new WuQuantizer <T, TP> {
                    Threshold = this.Threshold
                };
            }

            // Quantize the image returning a palette. This boxing is icky.
            QuantizedImage <T, TP> quantized = ((IQuantizer <T, TP>) this.Quantizer).Quantize(image, this.Quality);

            // Grab the palette and write it to the stream.
            T[] palette    = quantized.Palette;
            int pixelCount = palette.Length;

            // Get max colors for bit depth.
            int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3;

            byte[] colorTable = new byte[colorTableLength];

            Parallel.For(
                0,
                pixelCount,
                Bootstrapper.Instance.ParallelOptions,
                i =>
            {
                int offset   = i * 3;
                byte[] color = palette[i].ToBytes();

                // Expected format r->g->b
                colorTable[offset]     = color[0];
                colorTable[offset + 1] = color[1];
                colorTable[offset + 2] = color[2];
            });

            this.WriteChunk(stream, PngChunkTypes.Palette, colorTable);

            // Write the transparency data
            if (quantized.TransparentIndex > -1)
            {
                this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, new[] { (byte)quantized.TransparentIndex });
            }

            return(quantized);
        }
        public void Encode(ImageBase image, Stream stream)
        {
            if (image == null || stream == null)
            {
                throw new ArgumentNullException();
            }
            stream.Write(new byte[] {
                0x89,                // Set the high bit.
                0x50,                // P
                0x4E,                // N
                0x47,                // G
                0x0D,                // Line ending CRLF
                0x0A,                // Line ending CRLF
                0x1A,                // EOF
                0x0A                 // LF
            }, 0, 8);
            int quality = Quality > 0 ? Quality : image.Quality;

            Quality  = quality > 0 ? NumUtils.Clamp(quality, 1, int.MaxValue) : int.MaxValue;
            bitDepth = Quality <= 256
                                ? (byte)NumUtils.Clamp(GifEncoderCore.GetBitsNeededForColorDepth(Quality), 1, 8)
                                : (byte)8;
            if (bitDepth == 3)
            {
                bitDepth = 4;
            }
            else if (bitDepth >= 5 || bitDepth <= 7)
            {
                bitDepth = 8;
            }
            PngHeader header = new PngHeader {
                Width             = image.Width,
                Height            = image.Height,
                ColorType         = (byte)(Quality <= 256 ? 3 : 6),
                BitDepth          = bitDepth,
                FilterMethod      = 0,            // None
                CompressionMethod = 0,
                InterlaceMethod   = 0
            };

            WriteHeaderChunk(stream, header);
            QuantizedImage quantized = WritePaletteChunk(stream, header, image);

            WritePhysicalChunk(stream, image);
            WriteGammaChunk(stream);
            using (IPixelAccessor pixels = image.Lock()){
                WriteDataChunks(stream, pixels, quantized);
            }
            WriteEndChunk(stream);
            stream.Flush();
        }
Exemple #26
0
        private int GetTransparentIndex <TPixel>(QuantizedImage <TPixel> quantized)
            where TPixel : struct, IPixel <TPixel>
        {
            // Transparent pixels are much more likely to be found at the end of a palette
            int index = -1;
            var trans = default(Rgba32);

            for (int i = quantized.Palette.Length - 1; i >= 0; i--)
            {
                quantized.Palette[i].ToRgba32(ref trans);

                if (trans.Equals(default(Rgba32)))
                {
                    index = i;
                }
            }

            return(index);
        }
Exemple #27
0
        /// <summary>
        /// Loads the animation.
        /// </summary>
        /// <param name="animation">The animation.</param>
        /// <param name="quantizedImage">The quantized image.</param>
        private void LoadAnimation(Animation animation, QuantizedImage quantizedImage)
        {
            var TempImage         = animation[0];
            var TransparencyIndex = quantizedImage.TransparentIndex;

            Header           = new FileHeader();
            ScreenDescriptor = new LogicalScreenDescriptor(TempImage, TransparencyIndex, BitDepth);
            Frames.Add(new Frame(TempImage, quantizedImage, BitDepth, animation.Delay));
            if (animation.Count > 1)
            {
                AppExtension = new ApplicationExtension(animation.RepeatCount, animation.Count);
                for (int x = 1; x < animation.Count; ++x)
                {
                    quantizedImage = Quantizer.Quantize(animation[x], Quality);
                    TempImage      = animation[x];
                    Frames.Add(new Frame(TempImage, quantizedImage, BitDepth, animation.Delay));
                }
            }
        }
Exemple #28
0
        public void OctreeQuantizerYieldsCorrectTransparentPixel <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)));

                IQuantizer <TPixel> quantizer = new OctreeQuantizer <TPixel> {
                    Dither = dither
                };

                foreach (ImageFrame <TPixel> frame in image.Frames)
                {
                    QuantizedImage <TPixel> quantized = quantizer.Quantize(frame, 256);

                    int index = this.GetTransparentIndex(quantized);
                    Assert.Equal(index, quantized.Pixels[0]);
                }
            }
        }
Exemple #29
0
        /// <summary>
        /// Returns the index of the most transparent color in the palette.
        /// </summary>
        /// <param name="quantized">
        /// The quantized.
        /// </param>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        private static int GetTransparentIndex <TColor, TPacked>(QuantizedImage <TColor, TPacked> quantized)
            where TColor : struct, IPackedPixel <TPacked>
            where TPacked : struct
        {
            // Find the lowest alpha value and make it the transparent index.
            int   index = 255;
            float alpha = 1;

            for (int i = 0; i < quantized.Palette.Length; i++)
            {
                float a = quantized.Palette[i].ToVector4().W;
                if (a < alpha)
                {
                    alpha = a;
                    index = i;
                }
            }

            return(index);
        }
Exemple #30
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ColorTable"/> class.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="BitDepth">The bit depth.</param>
        public ColorTable(QuantizedImage image, int BitDepth)
        {
            Bgra[] palette    = image.Palette;
            int    pixelCount = palette.Length;

            // Get max colors for bit depth.
            int colorTableLength = (int)Math.Pow(2, BitDepth) * 3;

            byte[] colorTable = new byte[colorTableLength];

            Parallel.For(0, pixelCount,
                         i =>
            {
                int offset = i * 3;
                Bgra color = palette[i];

                colorTable[offset]     = color.Red;
                colorTable[offset + 1] = color.Green;
                colorTable[offset + 2] = color.Blue;
            });
            Data = colorTable;
        }