Beispiel #1
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);
                }
            }
        }
Beispiel #2
0
        public void ApplyQuantizationDither <TFrameQuantizer, TPixel>(
            ref TFrameQuantizer quantizer,
            ImageFrame <TPixel> source,
            IndexedImageFrame <TPixel> destination,
            Rectangle bounds)
            where TFrameQuantizer : struct, IQuantizer <TPixel>
            where TPixel : unmanaged, IPixel <TPixel>
        {
            if (this == default)
            {
                ThrowDefaultInstance();
            }

            int   spread = CalculatePaletteSpread(destination.Palette.Length);
            float scale  = quantizer.Options.DitherScale;

            for (int y = bounds.Top; y < bounds.Bottom; y++)
            {
                ReadOnlySpan <TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width);
                Span <byte>           destRow   = destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length);

                for (int x = 0; x < sourceRow.Length; x++)
                {
                    TPixel dithered = this.Dither(sourceRow[x], x, y, spread, scale);
                    destRow[x] = quantizer.GetQuantizedColor(dithered, out TPixel _);
                }
            }
        }
Beispiel #3
0
        private void EncodeGlobal <TPixel>(Image <TPixel> image, IndexedImageFrame <TPixel> quantized, int transparencyIndex, Stream stream)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            // The palette quantizer can reuse the same pixel map across multiple frames
            // since the palette is unchanging. This allows a reduction of memory usage across
            // multi frame gifs using a global palette.
            EuclideanPixelMap <TPixel> pixelMap = default;
            bool pixelMapSet = false;

            for (int i = 0; i < image.Frames.Count; i++)
            {
                ImageFrame <TPixel> frame         = image.Frames[i];
                ImageFrameMetadata  metadata      = frame.Metadata;
                GifFrameMetadata    frameMetadata = metadata.GetGifMetadata();
                this.WriteGraphicalControlExtension(frameMetadata, transparencyIndex, stream);
                this.WriteImageDescriptor(frame, false, stream);

                if (i == 0)
                {
                    this.WriteImageData(quantized, stream);
                }
                else
                {
                    if (!pixelMapSet)
                    {
                        pixelMapSet = true;
                        pixelMap    = new EuclideanPixelMap <TPixel>(this.configuration, quantized.Palette);
                    }

                    using var paletteFrameQuantizer = new PaletteFrameQuantizer <TPixel>(this.configuration, this.quantizer.Options, pixelMap);
                    using IndexedImageFrame <TPixel> paletteQuantized = paletteFrameQuantizer.QuantizeFrame(frame, frame.Bounds());
                    this.WriteImageData(paletteQuantized, stream);
                }
            }
        }
Beispiel #4
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 : unmanaged, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

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

            ImageMetadata metadata    = image.Metadata;
            PngMetadata   pngMetadata = metadata.GetPngMetadata();

            PngEncoderOptionsHelpers.AdjustOptions <TPixel>(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
            IndexedImageFrame <TPixel> quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);

            this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized);

            stream.Write(PngConstants.HeaderBytes);

            this.WriteHeaderChunk(stream);
            this.WritePaletteChunk(stream, quantized);
            this.WriteTransparencyChunk(stream, pngMetadata);
            this.WritePhysicalChunk(stream, metadata);
            this.WriteGammaChunk(stream);
            this.WriteExifChunk(stream, metadata);
            this.WriteTextChunks(stream, pngMetadata);
            this.WriteDataChunks(image.Frames.RootFrame, quantized, stream);
            this.WriteEndChunk(stream);
            stream.Flush();

            quantized?.Dispose();
        }
Beispiel #5
0
        /// <summary>
        /// Returns a span of bytes in the correct format for the PictEncoder
        /// </summary>
        /// <typeparam name="TPixel"></typeparam>
        /// <param name="image"></param>
        /// <param name="quantized"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private ReadOnlySpan <byte> GetScanLine <TPixel>(Image <TPixel> image, IndexedImageFrame <TPixel> quantized, int y) where TPixel : unmanaged, IPixel <TPixel>
        {
            if (quantized == null && options.IsIndexed)
            {
                throw new Exception("Quantized image expected for indexed images");
            }

            if (quantized != null)
            {
                return(quantized.GetPixelRowSpan(y));
            }

            var pixels     = image.GetPixelRowSpan(y);
            var pixelBytes = (int)options.PictBpp / 8;

            using IMemoryOwner <byte> row = memoryAllocator.Allocate <byte>(pixelBytes * image.Width);
            Span <byte> rowSpan           = row.Memory.Span;

            switch (options.PictBpp)
            {
            case PictBpp.Bit16:
                PixelOperations <TPixel> .Instance.ToBgra5551Bytes(configuration, pixels, rowSpan, pixels.Length);

                break;

            case PictBpp.Bit32:
            default:
                PixelOperations <TPixel> .Instance.ToBgra32Bytes(configuration, pixels, rowSpan, pixels.Length);

                break;
            }
            return(rowSpan);
        }
        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();
        }
Beispiel #7
0
        /// <summary>
        /// Returns the index of the most transparent color in the palette.
        /// </summary>
        /// <param name="quantized">The quantized frame.</param>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        private int GetTransparentIndex <TPixel>(IndexedImageFrame <TPixel> quantized)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            // Transparent pixels are much more likely to be found at the end of a palette.
            int index = -1;
            ReadOnlySpan <TPixel> paletteSpan = quantized.Palette.Span;

            using IMemoryOwner <Rgba32> rgbaOwner = quantized.Configuration.MemoryAllocator.Allocate <Rgba32>(paletteSpan.Length);
            Span <Rgba32> rgbaSpan = rgbaOwner.GetSpan();

            PixelOperations <TPixel> .Instance.ToRgba32(quantized.Configuration, paletteSpan, rgbaSpan);

            ref Rgba32 rgbaSpanRef = ref MemoryMarshal.GetReference(rgbaSpan);
Beispiel #8
0
        /// <summary>
        /// Returns a PaletteEntry array from a quantized image
        /// </summary>
        /// <typeparam name="TPixel"></typeparam>
        /// <param name="quantized">Quantized image</param>
        /// <returns>Array of Palette Entries</returns>
        private PaletteEntry[] GetPalette <TPixel>(IndexedImageFrame <TPixel> quantized) where TPixel : unmanaged, IPixel <TPixel>
        {
            ReadOnlySpan <TPixel> sourcePalette = quantized.Palette.Span;
            int paletteLength = sourcePalette.Length;

            PaletteEntry[] destinationPalette = new PaletteEntry[paletteLength];
            for (int i = 0; i < paletteLength; i++)
            {
                Rgba32 rgba = new Rgba32();
                sourcePalette[i].ToRgba32(ref rgba);
                destinationPalette[i] = new PaletteEntry(rgba.A, rgba.R, rgba.G, rgba.B);
            }
            return(destinationPalette);
        }
Beispiel #9
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 IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(this.configuration, options);
                        quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
                    }
                    else
                    {
                        using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(this.configuration);
                        quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
                    }
                }

                this.bitDepth = ImageMaths.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;
            }
        }
Beispiel #10
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 IFrameQuantizer <Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer <Rgba32>(config);
            using IndexedImageFrame <Rgba32> result       = frameQuantizer.QuantizeFrame(frame, frame.Bounds());

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

            Assert.Equal(default, result.Palette.Span[0]);
Beispiel #11
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 : unmanaged, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

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

            ImageMetadata metadata = image.Metadata;

            PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            PngEncoderOptionsHelpers.AdjustOptions <TPixel>(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
            Image <TPixel> clonedImage       = null;
            bool           clearTransparency = this.options.TransparentColorMode == PngTransparentColorMode.Clear;

            if (clearTransparency)
            {
                clonedImage = image.Clone();
                ClearTransparentPixels(clonedImage);
            }

            IndexedImageFrame <TPixel> quantized = this.CreateQuantizedImage(image, clonedImage);

            stream.Write(PngConstants.HeaderBytes);

            this.WriteHeaderChunk(stream);
            this.WriteGammaChunk(stream);
            this.WritePaletteChunk(stream, quantized);
            this.WriteTransparencyChunk(stream, pngMetadata);
            this.WritePhysicalChunk(stream, metadata);
            this.WriteExifChunk(stream, metadata);
            this.WriteTextChunks(stream, pngMetadata);
            this.WriteDataChunks(clearTransparency ? clonedImage : image, quantized, stream);
            this.WriteEndChunk(stream);

            stream.Flush();

            quantized?.Dispose();
            clonedImage?.Dispose();
        }
Beispiel #12
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 IndexedImageFrame <Rgba32> result       = frameQuantizer.QuantizeFrame(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]);
        }
Beispiel #13
0
        public void ApplyQuantizationDither <TFrameQuantizer, TPixel>(
            ref TFrameQuantizer quantizer,
            ImageFrame <TPixel> source,
            IndexedImageFrame <TPixel> destination,
            Rectangle bounds)
            where TFrameQuantizer : struct, IQuantizer <TPixel>
            where TPixel : unmanaged, IPixel <TPixel>
        {
            var ditherOperation = new QuantizeDitherRowOperation <TFrameQuantizer, TPixel>(
                ref quantizer,
                in Unsafe.AsRef(this),
                source,
                destination,
                bounds);

            ParallelRowIterator.IterateRows(
                quantizer.Configuration,
                bounds,
                in ditherOperation);
        }
Beispiel #14
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);
                }
            }
        }
Beispiel #15
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);
                }
            }
        }
Beispiel #16
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);
                }
            }
        }
        /// <summary>
        /// Calculates the bit depth value.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="options">The options.</param>
        /// <param name="image">The image.</param>
        /// <param name="quantizedFrame">The quantized frame.</param>
        public static byte CalculateBitDepth <TPixel>(
            PngEncoderOptions options,
            Image <TPixel> image,
            IndexedImageFrame <TPixel> quantizedFrame)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            byte bitDepth;

            if (options.ColorType == PngColorType.Palette)
            {
                byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantizedFrame.Palette.Length).Clamp(1, 8);
                byte bits          = Math.Max((byte)options.BitDepth, quantizedBits);

                // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
                // We check again for the bit depth as the bit depth of the color palette from a given quantizer might not
                // be within the acceptable range.
                if (bits == 3)
                {
                    bits = 4;
                }
                else if (bits >= 5 && bits <= 7)
                {
                    bits = 8;
                }

                bitDepth = bits;
            }
            else
            {
                bitDepth = (byte)options.BitDepth;
            }

            if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bitDepth) == -1)
            {
                throw new NotSupportedException("Bit depth is not supported or not valid.");
            }

            return(bitDepth);
        }
Beispiel #18
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);
                }
            }
        }
Beispiel #19
0
        public static unsafe int RenderAnsi <TColor>(Stream o, IntPtr pixels,
                                                     uint w, uint h,
                                                     uint reduceLineCount = 0, int maxLineCount           = -1, int maxWidth = -1,
                                                     bool borderless      = false, AnsiColors colors      = AnsiColors.TrueColor,
                                                     IDither?customDither = null, float customDitherScale = 1f
                                                     ) where TColor : unmanaged, IPixel <TColor>
        {
            GetConsoleSize(out var cw, out var ch);

            if (maxWidth >= 0)
            {
                cw = maxWidth;
            }

            if (maxLineCount >= 0)
            {
                ch = maxLineCount;
            }

            cw -= 1;
            ch -= (int)reduceLineCount;

            if (cw == 0 || ch == 0)
            {
                return(0);
            }

            var aw = cw;
            var ah = ch * 2;

            var pPixels = (byte *)pixels;
            var span    = new ReadOnlySpan <TColor>(pPixels, checked ((int)(w * h)));

            using var img = Image.LoadPixelData(span, (int)w, (int)h);
            img.Mutate(x => x
                       .Resize(aw, ah, LanczosResampler.Lanczos3)
                       //.Crop(aw, ah)
                       );

            IndexedImageFrame <TColor>?indexedImg = null;
            var isTrueColor = colors == AnsiColors.TrueColor;

            if (!isTrueColor)
            {
                switch (colors)
                {
                case AnsiColors.Palette16: {
                    var opts = AnsiPalette16.Options;
                    if (customDither != null)
                    {
                        opts.Dither      = customDither;
                        opts.DitherScale = customDitherScale;
                    }

                    indexedImg = AnsiPalette16
                                 .CreatePixelSpecificQuantizer <TColor>(Configuration.Default, opts)
                                 .QuantizeFrame(img.Frames[0], new Rectangle(0, 0, img.Width, img.Height));
                    break;
                }

                case AnsiColors.Palette256: {
                    var opts = AnsiPalette256.Options;
                    if (customDither != null)
                    {
                        opts.Dither      = customDither;
                        opts.DitherScale = customDitherScale;
                    }

                    indexedImg = AnsiPalette256
                                 .CreatePixelSpecificQuantizer <TColor>(Configuration.Default, opts)
                                 .QuantizeFrame(img.Frames[0], new Rectangle(0, 0, img.Width, img.Height));
                    break;
                }
                }
            }

            void WriteNumberTriplet(byte b)
            {
                var ones        = b % 10;
                var tens        = b / 10 % 10;
                var hundreds    = b / 100;
                var anyHundreds = hundreds > 0;

                if (anyHundreds)
                {
                    o !.WriteByte((byte)('0' + hundreds));
                }
                if (anyHundreds || tens > 0)
                {
                    o !.WriteByte((byte)('0' + tens));
                }
                o !.WriteByte((byte)('0' + ones));
            }

            // ╭
            void DrawTopLeftCorner()
            {
                o.WriteByte(0xE2);
                o.WriteByte(0x95);
                o.WriteByte(0xAD);
            }

            // ╮
            void DrawTopRightCorner()
            {
                o.WriteByte(0xE2);
                o.WriteByte(0x95);
                o.WriteByte(0xAE);
            }

            // ╰
            void DrawBottomLeftCorner()
            {
                o.WriteByte(0xE2);
                o.WriteByte(0x95);
                o.WriteByte(0xB0);
            }

            // ╯
            void DrawBottomRightCorner()
            {
                o.WriteByte(0xE2);
                o.WriteByte(0x95);
                o.WriteByte(0xAF);
            }

            // ─ x width
            void DrawHorizontalFrame(int width)
            {
                for (var i = 0; i < width; ++i)
                {
                    o.WriteByte(0xE2);
                    o.WriteByte(0x94);
                    o.WriteByte(0x80);
                }
            }

            // │
            void DrawVerticalFrame()
            {
                o.WriteByte(0xE2);
                o.WriteByte(0x94);
                o.WriteByte(0x82);
            }

            if (!borderless)
            {
                DrawTopLeftCorner();
                DrawHorizontalFrame(aw + 1);
                DrawTopRightCorner();
                o.WriteByte((byte)'\n');
            }

            var lastY = ah & ~1;

            for (var y = 0; y < lastY; y += 2)
            {
                if (!borderless)
                {
                    DrawVerticalFrame();
                }
                // write 2 lines at a time
                var haveL = y + 1 < ah;

                var u = isTrueColor
          ? img.GetPixelRowSpan(y)
          : default;
                var l = haveL && isTrueColor
          ? img.GetPixelRowSpan(y + 1)
          : default;

                var up = !isTrueColor
          ? indexedImg !.GetPixelRowSpan(y)
          : default;