Ejemplo n.º 1
0
        /// <summary>
        /// Creates the quantized frame.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="options">The options.</param>
        /// <param name="image">The image.</param>
        public static IndexedImageFrame <TPixel> CreateQuantizedFrame <TPixel>(
            PngEncoderOptions options,
            Image <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            if (options.ColorType != PngColorType.Palette)
            {
                return(null);
            }

            // Use the metadata to determine what quantization depth to use if no quantizer has been set.
            if (options.Quantizer is null)
            {
                byte bits      = (byte)options.BitDepth;
                var  maxColors = ImageMaths.GetColorCountForBitDepth(bits);
                options.Quantizer = new WuQuantizer(new QuantizerOptions {
                    MaxColors = maxColors
                });
            }

            // Create quantized frame returning the palette and set the bit depth.
            using (IQuantizer <TPixel> frameQuantizer = options.Quantizer.CreatePixelSpecificQuantizer <TPixel>(image.GetConfiguration()))
            {
                ImageFrame <TPixel> frame = image.Frames.RootFrame;
                return(frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()));
            }
        }
        public TiffPaletteWriter(
            ImageFrame <TPixel> image,
            IQuantizer quantizer,
            MemoryAllocator memoryAllocator,
            Configuration configuration,
            TiffEncoderEntriesCollector entriesCollector,
            int bitsPerPixel)
            : base(image, memoryAllocator, configuration, entriesCollector)
        {
            DebugGuard.NotNull(quantizer, nameof(quantizer));
            DebugGuard.NotNull(configuration, nameof(configuration));
            DebugGuard.NotNull(entriesCollector, nameof(entriesCollector));
            DebugGuard.MustBeBetweenOrEqualTo(bitsPerPixel, 4, 8, nameof(bitsPerPixel));

            this.BitsPerPixel      = bitsPerPixel;
            this.maxColors         = this.BitsPerPixel == 4 ? 16 : 256;
            this.colorPaletteSize  = this.maxColors * 3;
            this.colorPaletteBytes = this.colorPaletteSize * 2;
            using IQuantizer <TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer <TPixel>(this.Configuration, new QuantizerOptions()
            {
                MaxColors = this.maxColors
            });
            this.quantizedImage = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());

            this.AddColorMapTag();
        }
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);

            Configuration configuration = this.Configuration;

            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(source, interest);

            ReadOnlySpan <TPixel> paletteSpan = quantized.Palette.Span;
            int offsetY = interest.Top;
            int offsetX = interest.Left;
            Buffer2D <TPixel> sourceBuffer = source.PixelBuffer;

            for (int y = interest.Y; y < interest.Height; y++)
            {
                Span <TPixel>       row          = sourceBuffer.DangerousGetRowSpan(y);
                ReadOnlySpan <byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY);

                for (int x = interest.Left; x < interest.Right; x++)
                {
                    row[x] = paletteSpan[quantizedRow[x - offsetX]];
                }
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Writes an 8 Bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="stream">The <see cref="Stream"/> to write to.</param>
        /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
        /// <param name="colorPalette">A byte span of size 1024 for the color palette.</param>
        private void Write8BitColor <TPixel>(Stream stream, ImageFrame <TPixel> image, Span <byte> colorPalette)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());

            ReadOnlySpan <TPixel> quantizedColors = quantized.Palette.Span;

            PixelOperations <TPixel> .Instance.ToBgra32(this.configuration, quantizedColors, MemoryMarshal.Cast <byte, Bgra32>(colorPalette));

            Span <uint> colorPaletteAsUInt = MemoryMarshal.Cast <byte, uint>(colorPalette);

            for (int i = 0; i < colorPaletteAsUInt.Length; i++)
            {
                colorPaletteAsUInt[i] = colorPaletteAsUInt[i] & 0x00FFFFFF; // Padding byte, always 0.
            }

            stream.Write(colorPalette);

            for (int y = image.Height - 1; y >= 0; y--)
            {
                ReadOnlySpan <byte> pixelSpan = quantized.GetPixelRowSpan(y);
                stream.Write(pixelSpan);

                for (int i = 0; i < this.padding; i++)
                {
                    stream.WriteByte(0);
                }
            }
        }
Ejemplo n.º 5
0
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);

            Configuration configuration = this.Configuration;

            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(source, interest);

            var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized);

            ParallelRowIterator.IterateRowIntervals(
                configuration,
                interest,
                in operation);
        }
Ejemplo n.º 6
0
        private void EncodeLocal <TPixel>(Image <TPixel> image, IndexedImageFrame <TPixel> quantized, Stream stream)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            ImageFrame <TPixel> previousFrame = null;
            GifFrameMetadata    previousMeta  = null;

            for (int i = 0; i < image.Frames.Count; i++)
            {
                ImageFrame <TPixel> frame         = image.Frames[i];
                ImageFrameMetadata  metadata      = frame.Metadata;
                GifFrameMetadata    frameMetadata = metadata.GetGifMetadata();
                if (quantized is null)
                {
                    // Allow each frame to be encoded at whatever color depth the frame designates if set.
                    if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength &&
                        frameMetadata.ColorTableLength > 0)
                    {
                        var options = new QuantizerOptions
                        {
                            Dither      = this.quantizer.Options.Dither,
                            DitherScale = this.quantizer.Options.DitherScale,
                            MaxColors   = frameMetadata.ColorTableLength
                        };

                        using IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration, options);
                        quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds());
                    }
                    else
                    {
                        using IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration);
                        quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds());
                    }
                }

                this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
                this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream);
                this.WriteImageDescriptor(frame, true, stream);
                this.WriteColorTable(quantized, stream);
                this.WriteImageData(quantized, stream);

                quantized.Dispose();
                quantized     = null; // So next frame can regenerate it
                previousFrame = frame;
                previousMeta  = frameMetadata;
            }
        }
Ejemplo n.º 7
0
        public void SinglePixelTransparent()
        {
            Configuration config    = Configuration.Default;
            var           quantizer = new WuQuantizer(new QuantizerOptions {
                Dither = null
            });

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

            using IQuantizer <Rgba32> frameQuantizer = quantizer.CreatePixelSpecificQuantizer <Rgba32>(config);
            using IndexedImageFrame <Rgba32> result  = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds());

            Assert.Equal(1, result.Palette.Length);
            Assert.Equal(1, result.Width);
            Assert.Equal(1, result.Height);

            Assert.Equal(default, result.Palette.Span[0]);
Ejemplo n.º 8
0
        /// <summary>
        /// Creates the quantized image and sets calculates and sets the bit depth.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="image">The image to quantize.</param>
        /// <returns>The quantized image.</returns>
        private IndexedImageFrame <TPixel> CreateQuantizedImage <TPixel>(Image <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            // Use the metadata to determine what quantization depth to use if no quantizer has been set.
            if (options.Quantizer is null)
            {
                byte bits      = (byte)options.PictBpp;
                var  maxColors = GetColorCountForBitDepth(bits);
                options.Quantizer = new WuQuantizer(new QuantizerOptions {
                    MaxColors = maxColors
                });
            }

            // Create quantized frame returning the palette and set the bit depth.
            using IQuantizer <TPixel> frameQuantizer = options.Quantizer.CreatePixelSpecificQuantizer <TPixel>(image.GetConfiguration());
            ImageFrame <TPixel> frame = image.Frames.RootFrame;

            return(frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()));
        }
Ejemplo n.º 9
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 IQuantizer <Rgba32> frameQuantizer = quantizer.CreatePixelSpecificQuantizer <Rgba32>(config);
            using IndexedImageFrame <Rgba32> result  = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds());

            Assert.Equal(1, result.Palette.Length);
            Assert.Equal(1, result.Width);
            Assert.Equal(1, result.Height);

            Assert.Equal(Color.Black, (Color)result.Palette.Span[0]);
            Assert.Equal(0, result.GetPixelRowSpan(0)[0]);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Writes a 1 bit image with a color palette. The color palette has 2 entry's with 4 bytes for each entry.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="stream">The <see cref="Stream"/> to write to.</param>
        /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
        private void Write1BitColor <TPixel>(Stream stream, ImageFrame <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration, new QuantizerOptions()
            {
                MaxColors = 2
            });
            using IndexedImageFrame <TPixel> quantized   = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());
            using IMemoryOwner <byte> colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize1Bit, AllocationOptions.Clean);

            Span <byte>           colorPalette          = colorPaletteBuffer.GetSpan();
            ReadOnlySpan <TPixel> quantizedColorPalette = quantized.Palette.Span;

            this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);

            ReadOnlySpan <byte> quantizedPixelRow = quantized.GetPixelRowSpan(0);
            int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding;

            for (int y = image.Height - 1; y >= 0; y--)
            {
                quantizedPixelRow = quantized.GetPixelRowSpan(y);

                int endIdx = quantizedPixelRow.Length % 8 == 0 ? quantizedPixelRow.Length : quantizedPixelRow.Length - 8;
                for (int i = 0; i < endIdx; i += 8)
                {
                    Write1BitPalette(stream, i, i + 8, quantizedPixelRow);
                }

                if (quantizedPixelRow.Length % 8 != 0)
                {
                    int startIdx = quantizedPixelRow.Length - 7;
                    endIdx = quantizedPixelRow.Length;
                    Write1BitPalette(stream, startIdx, endIdx, quantizedPixelRow);
                }

                for (int i = 0; i < rowPadding; i++)
                {
                    stream.WriteByte(0);
                }
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Writes an 8 bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="stream">The <see cref="Stream"/> to write to.</param>
        /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
        /// <param name="colorPalette">A byte span of size 1024 for the color palette.</param>
        private void Write8BitColor <TPixel>(Stream stream, ImageFrame <TPixel> image, Span <byte> colorPalette)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());

            ReadOnlySpan <TPixel> quantizedColorPalette = quantized.Palette.Span;

            this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);

            for (int y = image.Height - 1; y >= 0; y--)
            {
                ReadOnlySpan <byte> pixelSpan = quantized.GetPixelRowSpan(y);
                stream.Write(pixelSpan);

                for (int i = 0; i < this.padding; i++)
                {
                    stream.WriteByte(0);
                }
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Writes an 4 bit color image with a color palette. The color palette has 16 entry's with 4 bytes for each entry.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="stream">The <see cref="Stream"/> to write to.</param>
        /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
        private void Write4BitColor <TPixel>(Stream stream, ImageFrame <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration, new QuantizerOptions()
            {
                MaxColors = 16
            });
            using IndexedImageFrame <TPixel> quantized   = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());
            using IMemoryOwner <byte> colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize4Bit, AllocationOptions.Clean);

            Span <byte>           colorPalette          = colorPaletteBuffer.GetSpan();
            ReadOnlySpan <TPixel> quantizedColorPalette = quantized.Palette.Span;

            this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);

            ReadOnlySpan <byte> pixelRowSpan = quantized.GetPixelRowSpan(0);
            int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding;

            for (int y = image.Height - 1; y >= 0; y--)
            {
                pixelRowSpan = quantized.GetPixelRowSpan(y);

                int endIdx = pixelRowSpan.Length % 2 == 0 ? pixelRowSpan.Length : pixelRowSpan.Length - 1;
                for (int i = 0; i < endIdx; i += 2)
                {
                    stream.WriteByte((byte)((pixelRowSpan[i] << 4) | pixelRowSpan[i + 1]));
                }

                if (pixelRowSpan.Length % 2 != 0)
                {
                    stream.WriteByte((byte)((pixelRowSpan[pixelRowSpan.Length - 1] << 4) | 0));
                }

                for (int i = 0; i < rowPadding; i++)
                {
                    stream.WriteByte(0);
                }
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Writes an 8 Bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="stream">The <see cref="Stream"/> to write to.</param>
        /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
        /// <param name="colorPalette">A byte span of size 1024 for the color palette.</param>
        private void Write8BitColor <TPixel>(Stream stream, ImageFrame <TPixel> image, Span <byte> colorPalette)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());

            ReadOnlySpan <TPixel> quantizedColors = quantized.Palette.Span;
            var color = default(Rgba32);

            // TODO: Use bulk conversion here for better perf
            int idx = 0;

            foreach (TPixel quantizedColor in quantizedColors)
            {
                quantizedColor.ToRgba32(ref color);
                colorPalette[idx]     = color.B;
                colorPalette[idx + 1] = color.G;
                colorPalette[idx + 2] = color.R;

                // Padding byte, always 0.
                colorPalette[idx + 3] = 0;
                idx += 4;
            }

            stream.Write(colorPalette);

            for (int y = image.Height - 1; y >= 0; y--)
            {
                ReadOnlySpan <byte> pixelSpan = quantized.GetPixelRowSpan(y);
                stream.Write(pixelSpan);

                for (int i = 0; i < this.padding; i++)
                {
                    stream.WriteByte(0);
                }
            }
        }
Ejemplo n.º 14
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="Image{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        /// <param name="cancellationToken">The token to request cancellation.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream, CancellationToken cancellationToken)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            ImageMetadata metadata    = image.Metadata;
            GifMetadata   gifMetadata = metadata.GetGifMetadata();

            this.colorTableMode ??= gifMetadata.ColorTableMode;
            bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;

            // Quantize the image returning a palette.
            IndexedImageFrame <TPixel> quantized;

            using (IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(this.configuration))
            {
                if (useGlobalTable)
                {
                    frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
                    quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
                }
                else
                {
                    quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds());
                }
            }

            // Get the number of bits.
            this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);

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

            // Write the LSD.
            int index = this.GetTransparentIndex(quantized);

            this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream);

            if (useGlobalTable)
            {
                this.WriteColorTable(quantized, stream);
            }

            // Write the comments.
            this.WriteComments(gifMetadata, stream);

            // Write application extension to allow additional frames.
            if (image.Frames.Count > 1)
            {
                this.WriteApplicationExtension(stream, gifMetadata.RepeatCount);
            }

            if (useGlobalTable)
            {
                this.EncodeGlobal(image, quantized, index, stream);
            }
            else
            {
                this.EncodeLocal(image, quantized, stream);
            }

            // Clean up.
            quantized.Dispose();

            // TODO: Write extension etc
            stream.WriteByte(GifConstants.EndIntroducer);
        }