/// <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())); } }
/// <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); }
/// <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; }