diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 7d00e20e4b..d8509548ad 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -144,7 +144,14 @@ namespace SixLabors.ImageSharp.Formats.Png PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance); PngEncoderOptionsHelpers.AdjustOptions(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel); - IndexedImageFrame quantized = this.CreateQuantizedImage(image); + Image clonedImage = null; + if (this.options.MakeTransparentBlack) + { + clonedImage = image.Clone(); + MakeTransparentPixelsBlack(clonedImage); + } + + IndexedImageFrame quantized = this.CreateQuantizedImage(image, clonedImage); stream.Write(PngConstants.HeaderBytes); @@ -155,12 +162,55 @@ namespace SixLabors.ImageSharp.Formats.Png this.WritePhysicalChunk(stream, metadata); this.WriteExifChunk(stream, metadata); this.WriteTextChunks(stream, pngMetadata); - this.WriteDataChunks(image.Frames.RootFrame, quantized, stream); + this.WriteDataChunks(this.options.MakeTransparentBlack ? clonedImage : image, quantized, stream); this.WriteEndChunk(stream); stream.Flush(); quantized?.Dispose(); + clonedImage?.Dispose(); + } + + /// + public void Dispose() + { + this.previousScanline?.Dispose(); + this.currentScanline?.Dispose(); + this.subFilter?.Dispose(); + this.averageFilter?.Dispose(); + this.paethFilter?.Dispose(); + this.filterBuffer?.Dispose(); + + this.previousScanline = null; + this.currentScanline = null; + this.subFilter = null; + this.averageFilter = null; + this.paethFilter = null; + this.filterBuffer = null; + } + + /// + /// Makes transparent pixels black. + /// + /// The type of the pixel. + /// The cloned image where the transparent pixels will be changed. + private static void MakeTransparentPixelsBlack(Image image) + where TPixel : unmanaged, IPixel + { + Rgba32 rgba32 = default; + for (int y = 0; y < image.Height; y++) + { + Span span = image.GetPixelRowSpan(y); + for (int x = 0; x < image.Width; x++) + { + span[x].ToRgba32(ref rgba32); + + if (rgba32.A == 0) + { + span[x].FromRgba32(Color.Black); + } + } + } } /// @@ -168,37 +218,16 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The type of the pixel. /// The image to quantize. + /// Cloned image with transparent pixels are changed to black. /// The quantized image. - private IndexedImageFrame CreateQuantizedImage(Image image) + private IndexedImageFrame CreateQuantizedImage(Image image, Image clonedImage) where TPixel : unmanaged, IPixel { IndexedImageFrame quantized; if (this.options.MakeTransparentBlack) { - using (Image tempImage = image.Clone()) - { - for (int y = 0; y < image.Height; y++) - { - Span span = tempImage.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - Rgba32 rgba32 = default; - span[x].ToRgba32(ref rgba32); - - if (rgba32.A == 0) - { - rgba32.R = 0; - rgba32.G = 0; - rgba32.B = 0; - } - - span[x].FromRgba32(rgba32); - } - } - - quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, tempImage); - this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, tempImage, quantized); - } + quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, clonedImage); + this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, clonedImage, quantized); } else { @@ -209,24 +238,6 @@ namespace SixLabors.ImageSharp.Formats.Png return quantized; } - /// - public void Dispose() - { - this.previousScanline?.Dispose(); - this.currentScanline?.Dispose(); - this.subFilter?.Dispose(); - this.averageFilter?.Dispose(); - this.paethFilter?.Dispose(); - this.filterBuffer?.Dispose(); - - this.previousScanline = null; - this.currentScanline = null; - this.subFilter = null; - this.averageFilter = null; - this.paethFilter = null; - this.filterBuffer = null; - } - /// Collects a row of grayscale pixels. /// The pixel format. /// The image row span. @@ -859,7 +870,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image. /// The quantized pixel data. Can be null. /// The stream. - private void WriteDataChunks(ImageFrame pixels, IndexedImageFrame quantized, Stream stream) + private void WriteDataChunks(Image pixels, IndexedImageFrame quantized, Stream stream) where TPixel : unmanaged, IPixel { byte[] buffer; @@ -957,8 +968,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixels. /// The quantized pixels span. /// The deflate stream. - private void EncodePixels(ImageFrame pixels, IndexedImageFrame quantized, ZlibDeflateStream deflateStream) - where TPixel : unmanaged, IPixel + private void EncodePixels(Image pixels, IndexedImageFrame quantized, ZlibDeflateStream deflateStream) + where TPixel : unmanaged, IPixel { int bytesPerScanline = this.CalculateScanlineLength(this.width); int resultLength = bytesPerScanline + 1; @@ -981,7 +992,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The type of the pixel. /// The pixels. /// The deflate stream. - private void EncodeAdam7Pixels(ImageFrame pixels, ZlibDeflateStream deflateStream) + private void EncodeAdam7Pixels(Image pixels, ZlibDeflateStream deflateStream) where TPixel : unmanaged, IPixel { int width = pixels.Width;