diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 0f416cb7b0..38c3484c81 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -59,8 +59,8 @@ namespace SixLabors.ImageSharp.Formats.Png PngInterlaceMode? InterlaceMethod { get; } /// - /// Gets a value indicating whether this instance should skip certain chunks to decrease file size + /// Gets the optimize method. /// - bool Optimized { get; } + PngOptimizeMethod? OptimizeMethod { get; } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index 5a79915de1..16bd538c31 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -63,9 +63,9 @@ namespace SixLabors.ImageSharp.Formats.Png public PngInterlaceMode? InterlaceMethod { get; set; } /// - /// Gets or sets a value indicating whether this instance should skip certain chunks to decrease file size + /// Gets or sets the optimize method. /// - public bool Optimized { get; set; } + public PngOptimizeMethod? OptimizeMethod { get; set; } /// /// Encodes the image to the specified stream from the . diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 8833389e7c..02bb67c728 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -153,14 +153,26 @@ namespace SixLabors.ImageSharp.Formats.Png stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length); this.WriteHeaderChunk(stream); - this.WritePaletteChunk(stream, quantized, this.options.Optimized); + this.WritePaletteChunk(stream, quantized, this.options.OptimizeMethod); this.WriteTransparencyChunk(stream, pngMetadata); - if (!this.options.Optimized) + if (((this.options.OptimizeMethod ?? PngOptimizeMethod.None) & PngOptimizeMethod.SuppressPhysicalChunk) != PngOptimizeMethod.SuppressPhysicalChunk) { this.WritePhysicalChunk(stream, metadata); + } + + if (((this.options.OptimizeMethod ?? PngOptimizeMethod.None) & PngOptimizeMethod.SuppressGammaChunk) != PngOptimizeMethod.SuppressGammaChunk) + { this.WriteGammaChunk(stream); + } + + if (((this.options.OptimizeMethod ?? PngOptimizeMethod.None) & PngOptimizeMethod.SuppressExifChunk) != PngOptimizeMethod.SuppressExifChunk) + { this.WriteExifChunk(stream, metadata); + } + + if (((this.options.OptimizeMethod ?? PngOptimizeMethod.None) & PngOptimizeMethod.SuppressTextChunks) != PngOptimizeMethod.SuppressTextChunks) + { this.WriteTextChunks(stream, pngMetadata); } @@ -552,8 +564,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The containing image data. /// The quantized frame. - /// If optimized make fully transparent pixels black. - private void WritePaletteChunk(Stream stream, IQuantizedFrame quantized, bool optimized) + /// The optimize method. + private void WritePaletteChunk(Stream stream, IQuantizedFrame quantized, PngOptimizeMethod? optimizeMethod) where TPixel : struct, IPixel { if (quantized == null) @@ -576,6 +588,8 @@ namespace SixLabors.ImageSharp.Formats.Png Rgba32 rgba = default; + bool makeTransparentBlack = ((optimizeMethod ?? PngOptimizeMethod.None) & PngOptimizeMethod.MakeTransparentBlack) == PngOptimizeMethod.MakeTransparentBlack; + for (int i = 0; i < paletteLength; i++) { if (quantizedSpan.IndexOf((byte)i) > -1) @@ -585,9 +599,18 @@ namespace SixLabors.ImageSharp.Formats.Png byte alpha = rgba.A; - Unsafe.Add(ref colorTableRef, offset) = optimized && alpha == 0 ? (byte)0 : rgba.R; - Unsafe.Add(ref colorTableRef, offset + 1) = optimized && alpha == 0 ? (byte)0 : rgba.G; - Unsafe.Add(ref colorTableRef, offset + 2) = optimized && alpha == 0 ? (byte)0 : rgba.B; + if (makeTransparentBlack && alpha == 0) + { + Unsafe.Add(ref colorTableRef, offset) = 0; + Unsafe.Add(ref colorTableRef, offset + 1) = 0; + Unsafe.Add(ref colorTableRef, offset + 2) = 0; + } + else + { + Unsafe.Add(ref colorTableRef, offset) = rgba.R; + Unsafe.Add(ref colorTableRef, offset + 1) = rgba.G; + Unsafe.Add(ref colorTableRef, offset + 2) = rgba.B; + } if (alpha > this.options.Threshold) { diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs index 5d4be8f149..89d7b0d5ef 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.Quantizer = source.Quantizer; this.Threshold = source.Threshold; this.InterlaceMethod = source.InterlaceMethod; - this.Optimized = source.Optimized; + this.OptimizeMethod = source.OptimizeMethod; } /// @@ -81,8 +81,8 @@ namespace SixLabors.ImageSharp.Formats.Png public PngInterlaceMode? InterlaceMethod { get; set; } /// - /// Gets or sets a value indicating whether this instance should skip certain chunks to decrease file size + /// Gets or sets a the optimize method. /// - public bool Optimized { get; set; } + public PngOptimizeMethod? OptimizeMethod { get; set; } } } diff --git a/src/ImageSharp/Formats/Png/PngOptimizeMethod.cs b/src/ImageSharp/Formats/Png/PngOptimizeMethod.cs new file mode 100644 index 0000000000..7ad6646380 --- /dev/null +++ b/src/ImageSharp/Formats/Png/PngOptimizeMethod.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Png +{ + /// + /// Provides enumeration of available PNG optimization methods. + /// + [Flags] + public enum PngOptimizeMethod + { + /// + /// With the None filter, the scanline is transmitted unmodified. + /// + None = 0, + + /// + /// Suppress the physical dimension information chunk. + /// + SuppressPhysicalChunk = 1, + + /// + /// Suppress the gamma information chunk. + /// + SuppressGammaChunk = 2, + + /// + /// Suppress the eXIf chunk. + /// + SuppressExifChunk = 4, + + /// + /// Suppress the tTXt, iTXt or zTXt chunk. + /// + SuppressTextChunks = 8, + + /// + /// Make funlly transparent pixels black. + /// + MakeTransparentBlack = 16, + + /// + /// All possible optimizations. + /// + All = 31, + } +}