77using System . IO ;
88using System . Runtime . CompilerServices ;
99using System . Runtime . InteropServices ;
10- using SixLabors . ImageSharp . Advanced ;
10+
1111using SixLabors . ImageSharp . Formats . Png . Chunks ;
1212using SixLabors . ImageSharp . Formats . Png . Filters ;
1313using SixLabors . ImageSharp . Formats . Png . Zlib ;
@@ -141,10 +141,18 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
141141 this . height = image . Height ;
142142
143143 ImageMetadata metadata = image . Metadata ;
144- PngMetadata pngMetadata = metadata . GetPngMetadata ( ) ;
144+
145+ PngMetadata pngMetadata = metadata . GetFormatMetadata ( PngFormat . Instance ) ;
145146 PngEncoderOptionsHelpers . AdjustOptions < TPixel > ( this . options , pngMetadata , out this . use16Bit , out this . bytesPerPixel ) ;
146- IndexedImageFrame < TPixel > quantized = PngEncoderOptionsHelpers . CreateQuantizedFrame ( this . options , image ) ;
147- this . bitDepth = PngEncoderOptionsHelpers . CalculateBitDepth ( this . options , image , quantized ) ;
147+ Image < TPixel > clonedImage = null ;
148+ bool clearTransparency = this . options . TransparentColorMode == PngTransparentColorMode . Clear ;
149+ if ( clearTransparency )
150+ {
151+ clonedImage = image . Clone ( ) ;
152+ ClearTransparentPixels ( clonedImage ) ;
153+ }
154+
155+ IndexedImageFrame < TPixel > quantized = this . CreateQuantizedImage ( image , clonedImage ) ;
148156
149157 stream . Write ( PngConstants . HeaderBytes ) ;
150158
@@ -155,11 +163,13 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
155163 this . WritePhysicalChunk ( stream , metadata ) ;
156164 this . WriteExifChunk ( stream , metadata ) ;
157165 this . WriteTextChunks ( stream , pngMetadata ) ;
158- this . WriteDataChunks ( image . Frames . RootFrame , quantized , stream ) ;
166+ this . WriteDataChunks ( clearTransparency ? clonedImage : image , quantized , stream ) ;
159167 this . WriteEndChunk ( stream ) ;
168+
160169 stream . Flush ( ) ;
161170
162171 quantized ? . Dispose ( ) ;
172+ clonedImage ? . Dispose ( ) ;
163173 }
164174
165175 /// <inheritdoc />
@@ -180,6 +190,55 @@ public void Dispose()
180190 this . filterBuffer = null ;
181191 }
182192
193+ /// <summary>
194+ /// Convert transparent pixels, to transparent black pixels, which can yield to better compression in some cases.
195+ /// </summary>
196+ /// <typeparam name="TPixel">The type of the pixel.</typeparam>
197+ /// <param name="image">The cloned image where the transparent pixels will be changed.</param>
198+ private static void ClearTransparentPixels < TPixel > ( Image < TPixel > image )
199+ where TPixel : unmanaged, IPixel < TPixel >
200+ {
201+ Rgba32 rgba32 = default ;
202+ for ( int y = 0 ; y < image . Height ; y ++ )
203+ {
204+ Span < TPixel > span = image . GetPixelRowSpan ( y ) ;
205+ for ( int x = 0 ; x < image . Width ; x ++ )
206+ {
207+ span [ x ] . ToRgba32 ( ref rgba32 ) ;
208+
209+ if ( rgba32 . A == 0 )
210+ {
211+ span [ x ] . FromRgba32 ( Color . Transparent ) ;
212+ }
213+ }
214+ }
215+ }
216+
217+ /// <summary>
218+ /// Creates the quantized image and sets calculates and sets the bit depth.
219+ /// </summary>
220+ /// <typeparam name="TPixel">The type of the pixel.</typeparam>
221+ /// <param name="image">The image to quantize.</param>
222+ /// <param name="clonedImage">Cloned image with transparent pixels are changed to black.</param>
223+ /// <returns>The quantized image.</returns>
224+ private IndexedImageFrame < TPixel > CreateQuantizedImage < TPixel > ( Image < TPixel > image , Image < TPixel > clonedImage )
225+ where TPixel : unmanaged, IPixel < TPixel >
226+ {
227+ IndexedImageFrame < TPixel > quantized ;
228+ if ( this . options . TransparentColorMode == PngTransparentColorMode . Clear )
229+ {
230+ quantized = PngEncoderOptionsHelpers . CreateQuantizedFrame ( this . options , clonedImage ) ;
231+ this . bitDepth = PngEncoderOptionsHelpers . CalculateBitDepth ( this . options , quantized ) ;
232+ }
233+ else
234+ {
235+ quantized = PngEncoderOptionsHelpers . CreateQuantizedFrame ( this . options , image ) ;
236+ this . bitDepth = PngEncoderOptionsHelpers . CalculateBitDepth ( this . options , quantized ) ;
237+ }
238+
239+ return quantized ;
240+ }
241+
183242 /// <summary>Collects a row of grayscale pixels.</summary>
184243 /// <typeparam name="TPixel">The pixel format.</typeparam>
185244 /// <param name="rowSpan">The image row span.</param>
@@ -602,6 +661,11 @@ private void WritePaletteChunk<TPixel>(Stream stream, IndexedImageFrame<TPixel>
602661 /// <param name="meta">The image metadata.</param>
603662 private void WritePhysicalChunk ( Stream stream , ImageMetadata meta )
604663 {
664+ if ( ( ( this . options . ChunkFilter ?? PngChunkFilter . None ) & PngChunkFilter . ExcludePhysicalChunk ) == PngChunkFilter . ExcludePhysicalChunk )
665+ {
666+ return ;
667+ }
668+
605669 PhysicalChunkData . FromMetadata ( meta ) . WriteTo ( this . chunkDataBuffer ) ;
606670
607671 this . WriteChunk ( stream , PngChunkType . Physical , this . chunkDataBuffer , 0 , PhysicalChunkData . Size ) ;
@@ -614,6 +678,11 @@ private void WritePhysicalChunk(Stream stream, ImageMetadata meta)
614678 /// <param name="meta">The image metadata.</param>
615679 private void WriteExifChunk ( Stream stream , ImageMetadata meta )
616680 {
681+ if ( ( ( this . options . ChunkFilter ?? PngChunkFilter . None ) & PngChunkFilter . ExcludeExifChunk ) == PngChunkFilter . ExcludeExifChunk )
682+ {
683+ return ;
684+ }
685+
617686 if ( meta . ExifProfile is null || meta . ExifProfile . Values . Count == 0 )
618687 {
619688 return ;
@@ -631,6 +700,11 @@ private void WriteExifChunk(Stream stream, ImageMetadata meta)
631700 /// <param name="meta">The image metadata.</param>
632701 private void WriteTextChunks ( Stream stream , PngMetadata meta )
633702 {
703+ if ( ( ( this . options . ChunkFilter ?? PngChunkFilter . None ) & PngChunkFilter . ExcludeTextChunks ) == PngChunkFilter . ExcludeTextChunks )
704+ {
705+ return ;
706+ }
707+
634708 const int MaxLatinCode = 255 ;
635709 for ( int i = 0 ; i < meta . TextData . Count ; i ++ )
636710 {
@@ -723,6 +797,11 @@ private byte[] GetCompressedTextBytes(byte[] textBytes)
723797 /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
724798 private void WriteGammaChunk ( Stream stream )
725799 {
800+ if ( ( ( this . options . ChunkFilter ?? PngChunkFilter . None ) & PngChunkFilter . ExcludeGammaChunk ) == PngChunkFilter . ExcludeGammaChunk )
801+ {
802+ return ;
803+ }
804+
726805 if ( this . options . Gamma > 0 )
727806 {
728807 // 4-byte unsigned integer of gamma * 100,000.
@@ -792,7 +871,7 @@ private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata)
792871 /// <param name="pixels">The image.</param>
793872 /// <param name="quantized">The quantized pixel data. Can be null.</param>
794873 /// <param name="stream">The stream.</param>
795- private void WriteDataChunks < TPixel > ( ImageFrame < TPixel > pixels , IndexedImageFrame < TPixel > quantized , Stream stream )
874+ private void WriteDataChunks < TPixel > ( Image < TPixel > pixels , IndexedImageFrame < TPixel > quantized , Stream stream )
796875 where TPixel : unmanaged, IPixel < TPixel >
797876 {
798877 byte [ ] buffer ;
@@ -890,8 +969,8 @@ private void AllocateExtBuffers()
890969 /// <param name="pixels">The pixels.</param>
891970 /// <param name="quantized">The quantized pixels span.</param>
892971 /// <param name="deflateStream">The deflate stream.</param>
893- private void EncodePixels < TPixel > ( ImageFrame < TPixel > pixels , IndexedImageFrame < TPixel > quantized , ZlibDeflateStream deflateStream )
894- where TPixel : unmanaged, IPixel < TPixel >
972+ private void EncodePixels < TPixel > ( Image < TPixel > pixels , IndexedImageFrame < TPixel > quantized , ZlibDeflateStream deflateStream )
973+ where TPixel : unmanaged, IPixel < TPixel >
895974 {
896975 int bytesPerScanline = this . CalculateScanlineLength ( this . width ) ;
897976 int resultLength = bytesPerScanline + 1 ;
@@ -914,7 +993,7 @@ private void EncodePixels<TPixel>(ImageFrame<TPixel> pixels, IndexedImageFrame<T
914993 /// <typeparam name="TPixel">The type of the pixel.</typeparam>
915994 /// <param name="pixels">The pixels.</param>
916995 /// <param name="deflateStream">The deflate stream.</param>
917- private void EncodeAdam7Pixels < TPixel > ( ImageFrame < TPixel > pixels , ZlibDeflateStream deflateStream )
996+ private void EncodeAdam7Pixels < TPixel > ( Image < TPixel > pixels , ZlibDeflateStream deflateStream )
918997 where TPixel : unmanaged, IPixel < TPixel >
919998 {
920999 int width = pixels . Width ;
0 commit comments