/// <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()));
            }
        }
        /// <summary>
        /// Adjusts the options based upon the given metadata.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="pngMetadata">The PNG metadata.</param>
        /// <param name="use16Bit">if set to <c>true</c> [use16 bit].</param>
        /// <param name="bytesPerPixel">The bytes per pixel.</param>
        public static void AdjustOptions <TPixel>(
            PngEncoderOptions options,
            PngMetadata pngMetadata,
            out bool use16Bit,
            out int bytesPerPixel)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            // Always take the encoder options over the metadata values.
            options.Gamma ??= pngMetadata.Gamma;

            // Use options, then check metadata, if nothing set there then we suggest
            // a sensible default based upon the pixel format.
            options.ColorType ??= pngMetadata.ColorType ?? SuggestColorType <TPixel>();
            options.BitDepth ??= pngMetadata.BitDepth ?? SuggestBitDepth <TPixel>();

            // Ensure bit depth and color type are a supported combination.
            // Bit8 is the only bit depth supported by all color types.
            byte bits = (byte)options.BitDepth;

            byte[] validBitDepths = PngConstants.ColorTypes[options.ColorType.Value];
            if (Array.IndexOf(validBitDepths, bits) == -1)
            {
                options.BitDepth = PngBitDepth.Bit8;
            }

            options.InterlaceMethod ??= pngMetadata.InterlaceMethod;

            use16Bit      = options.BitDepth == PngBitDepth.Bit16;
            bytesPerPixel = CalculateBytesPerPixel(options.ColorType, use16Bit);

            if (options.IgnoreMetadata)
            {
                options.ChunkFilter = PngChunkFilter.ExcludeAll;
            }
        }
        /// <summary>
        /// Adjusts the options based upon the given metadata.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="pngMetadata">The PNG metadata.</param>
        /// <param name="use16Bit">if set to <c>true</c> [use16 bit].</param>
        /// <param name="bytesPerPixel">The bytes per pixel.</param>
        public static void AdjustOptions <TPixel>(
            PngEncoderOptions options,
            PngMetadata pngMetadata,
            out bool use16Bit,
            out int bytesPerPixel)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            // Always take the encoder options over the metadata values.
            options.Gamma ??= pngMetadata.Gamma;

            // Use options, then check metadata, if nothing set there then we suggest
            // a sensible default based upon the pixel format.
            options.ColorType ??= pngMetadata.ColorType ?? SuggestColorType <TPixel>();
            options.BitDepth ??= pngMetadata.BitDepth ?? SuggestBitDepth <TPixel>();

            options.InterlaceMethod ??= pngMetadata.InterlaceMethod;

            use16Bit      = options.BitDepth == PngBitDepth.Bit16;
            bytesPerPixel = CalculateBytesPerPixel(options.ColorType, use16Bit);

            if (options.IgnoreMetadata)
            {
                options.ChunkFilter = PngChunkFilter.ExcludeAll;
            }

            // Ensure we are not allowing impossible combinations.
            if (!PngConstants.ColorTypes.ContainsKey(options.ColorType.Value))
            {
                throw new NotSupportedException("Color type is not supported or not valid.");
            }
        }
        /// <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);
            }

            byte bits = (byte)options.BitDepth;

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

            // Use the metadata to determine what quantization depth to use if no quantizer has been set.
            if (options.Quantizer is null)
            {
                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 (IFrameQuantizer <TPixel> frameQuantizer = options.Quantizer.CreateFrameQuantizer <TPixel>(image.GetConfiguration()))
            {
                ImageFrame <TPixel> frame = image.Frames.RootFrame;
                return(frameQuantizer.QuantizeFrame(frame, frame.Bounds()));
            }
        }
Example #5
0
        /// <summary>
        /// Adjusts the options.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="pngMetadata">The PNG metadata.</param>
        /// <param name="use16Bit">if set to <c>true</c> [use16 bit].</param>
        /// <param name="bytesPerPixel">The bytes per pixel.</param>
        public static void AdjustOptions(
            PngEncoderOptions options,
            PngMetadata pngMetadata,
            out bool use16Bit,
            out int bytesPerPixel)
        {
            // Always take the encoder options over the metadata values.
            options.Gamma           = options.Gamma ?? pngMetadata.Gamma;
            options.ColorType       = options.ColorType ?? pngMetadata.ColorType;
            options.BitDepth        = options.BitDepth ?? pngMetadata.BitDepth;
            options.InterlaceMethod = options.InterlaceMethod ?? pngMetadata.InterlaceMethod;

            use16Bit      = options.BitDepth == PngBitDepth.Bit16;
            bytesPerPixel = CalculateBytesPerPixel(options.ColorType, use16Bit);

            // Ensure we are not allowing impossible combinations.
            if (!PngConstants.ColorTypes.ContainsKey(options.ColorType.Value))
            {
                throw new NotSupportedException("Color type is not supported or not valid.");
            }
        }
        /// <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);
        }
Example #7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PngEncoderCore" /> class.
 /// </summary>
 /// <param name="memoryAllocator">The <see cref="MemoryAllocator" /> to use for buffer allocations.</param>
 /// <param name="configuration">The configuration.</param>
 /// <param name="options">The options for influencing the encoder</param>
 public PngEncoderCore(MemoryAllocator memoryAllocator, Configuration configuration, PngEncoderOptions options)
 {
     this.memoryAllocator = memoryAllocator;
     this.configuration   = configuration;
     this.options         = options;
 }