From b798d8dc014a3fbb6b074a11d2137ba00404a754 Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 31 Oct 2018 00:41:07 +0100 Subject: [PATCH 1/9] Work in progress preserving isTrans data --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 97 ++++++++------------ src/ImageSharp/Formats/Png/PngEncoderCore.cs | 5 + src/ImageSharp/Formats/Png/PngMetaData.cs | 27 ++++++ src/ImageSharp/MetaData/ImageMetaData.cs | 1 + 4 files changed, 73 insertions(+), 57 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 11c4d831b..024bd6221 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -124,31 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Png /// private PngColorType pngColorType; - /// - /// Represents any color in an 8 bit Rgb24 encoded png that should be transparent - /// - private Rgb24 rgb24Trans; - - /// - /// Represents any color in a 16 bit Rgb24 encoded png that should be transparent - /// - private Rgb48 rgb48Trans; - - /// - /// Represents any color in an 8 bit grayscale encoded png that should be transparent - /// - private byte luminanceTrans; - - /// - /// Represents any color in a 16 bit grayscale encoded png that should be transparent - /// - private ushort luminance16Trans; - - /// - /// Whether the image has transparency chunk and markers were decoded - /// - private bool hasTrans; - /// /// The next chunk of data to return /// @@ -213,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Png using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) { deframeStream.AllocateNewBytes(chunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); + this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData); } break; @@ -226,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Png byte[] alpha = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); this.paletteAlpha = alpha; - this.AssignTransparentMarkers(alpha); + this.AssignTransparentMarkers(alpha, pngMetaData); break; case PngChunkType.Text: this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); @@ -331,7 +306,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ReadByteLittleEndian(ReadOnlySpan buffer, int offset) - => (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF)); + { + return (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF)); + } /// /// Attempts to convert a byte array to a new array where each value in the original array is represented by the @@ -496,16 +473,17 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The containing data. /// The pixel data. - private void ReadScanlines(Stream dataStream, ImageFrame image) + /// The png meta data + private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetaData pngMetaData) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(dataStream, image); + this.DecodeInterlacedPixelData(dataStream, image, pngMetaData); } else { - this.DecodePixelData(dataStream, image); + this.DecodePixelData(dataStream, image, pngMetaData); } } @@ -515,7 +493,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The image to decode to. - private void DecodePixelData(Stream compressedStream, ImageFrame image) + /// The png meta data + private void DecodePixelData(Stream compressedStream, ImageFrame image, PngMetaData pngMetaData) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -555,7 +534,7 @@ namespace SixLabors.ImageSharp.Formats.Png throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(scanlineSpan, image); + this.ProcessDefilteredScanline(scanlineSpan, image, pngMetaData); this.SwapBuffers(); this.currentRow++; @@ -569,7 +548,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The current image. - private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image) + /// The png meta data + private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image, PngMetaData pngMetaData) where TPixel : struct, IPixel { while (true) @@ -626,7 +606,7 @@ namespace SixLabors.ImageSharp.Formats.Png } Span rowSpan = image.GetPixelRowSpan(this.currentRow); - this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); + this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetaData, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); this.SwapBuffers(); @@ -654,7 +634,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The image - private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels) + /// The png meta data + private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetaData pngMetaData) where TPixel : struct, IPixel { Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); @@ -674,9 +655,9 @@ namespace SixLabors.ImageSharp.Formats.Png this.header, scanlineSpan, rowSpan, - this.hasTrans, - this.luminance16Trans, - this.luminanceTrans); + pngMetaData.HasTrans, + pngMetaData.Luminance16Trans, + pngMetaData.LuminanceTrans); break; @@ -708,9 +689,9 @@ namespace SixLabors.ImageSharp.Formats.Png rowSpan, this.bytesPerPixel, this.bytesPerSample, - this.hasTrans, - this.rgb48Trans, - this.rgb24Trans); + pngMetaData.HasTrans, + pngMetaData.Rgb48Trans, + pngMetaData.Rgb24Trans); break; @@ -735,9 +716,10 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The current image row. + /// The png meta data /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. - private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, int pixelOffset = 0, int increment = 1) + private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetaData pngMetaData, int pixelOffset = 0, int increment = 1) where TPixel : struct, IPixel { // Trim the first marker byte from the buffer @@ -757,9 +739,9 @@ namespace SixLabors.ImageSharp.Formats.Png rowSpan, pixelOffset, increment, - this.hasTrans, - this.luminance16Trans, - this.luminanceTrans); + pngMetaData.HasTrans, + pngMetaData.Luminance16Trans, + pngMetaData.LuminanceTrans); break; @@ -796,9 +778,9 @@ namespace SixLabors.ImageSharp.Formats.Png increment, this.bytesPerPixel, this.bytesPerSample, - this.hasTrans, - this.rgb48Trans, - this.rgb24Trans); + pngMetaData.HasTrans, + pngMetaData.Rgb48Trans, + pngMetaData.Rgb24Trans); break; @@ -822,7 +804,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// Decodes and assigns marker colors that identify transparent pixels in non indexed images /// /// The alpha tRNS array - private void AssignTransparentMarkers(ReadOnlySpan alpha) + /// The png meta data + private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetaData pngMetaData) { if (this.pngColorType == PngColorType.Rgb) { @@ -834,16 +817,16 @@ namespace SixLabors.ImageSharp.Formats.Png ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); - this.rgb48Trans = new Rgb48(rc, gc, bc); - this.hasTrans = true; + pngMetaData.Rgb48Trans = new Rgb48(rc, gc, bc); + pngMetaData.HasTrans = true; return; } byte r = ReadByteLittleEndian(alpha, 0); byte g = ReadByteLittleEndian(alpha, 2); byte b = ReadByteLittleEndian(alpha, 4); - this.rgb24Trans = new Rgb24(r, g, b); - this.hasTrans = true; + pngMetaData.Rgb24Trans = new Rgb24(r, g, b); + pngMetaData.HasTrans = true; } } else if (this.pngColorType == PngColorType.Grayscale) @@ -852,14 +835,14 @@ namespace SixLabors.ImageSharp.Formats.Png { if (this.header.BitDepth == 16) { - this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); + pngMetaData.Luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); } else { - this.luminanceTrans = ReadByteLittleEndian(alpha, 0); + pngMetaData.LuminanceTrans = ReadByteLittleEndian(alpha, 0); } - this.hasTrans = true; + pngMetaData.HasTrans = true; } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 7ae716aa0..411d5d69b 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -290,6 +290,11 @@ namespace SixLabors.ImageSharp.Formats.Png this.WritePaletteChunk(stream, quantized); } + if (pngMetaData.HasTrans) + { + //Write transparency header + } + this.WritePhysicalChunk(stream, metaData); this.WriteGammaChunk(stream); this.WriteExifChunk(stream, metaData); diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs index 9c7676514..0014defbb 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetaData.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Png { /// @@ -42,6 +44,31 @@ namespace SixLabors.ImageSharp.Formats.Png /// public float Gamma { get; set; } + /// + /// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent + /// + public Rgb24 Rgb24Trans { get; set; } + + /// + /// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent + /// + public Rgb48 Rgb48Trans { get; set; } + + /// + /// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent + /// + public byte LuminanceTrans { get; set; } + + /// + /// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent + /// + public ushort Luminance16Trans { get; set; } + + /// + /// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded + /// + public bool HasTrans { get; set; } + /// public IDeepCloneable DeepClone() => new PngMetaData(this); } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index 73549d98a..ec9037479 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.MetaData { From e65fc5481f1617de7076f3ced7b54803633e772a Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 31 Oct 2018 01:14:09 +0100 Subject: [PATCH 2/9] This should write transparency --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 16 +++--- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 56 ++++++++++++++++++-- src/ImageSharp/Formats/Png/PngMetaData.cs | 8 +-- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 024bd6221..87c22a2ad 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -656,8 +656,8 @@ namespace SixLabors.ImageSharp.Formats.Png scanlineSpan, rowSpan, pngMetaData.HasTrans, - pngMetaData.Luminance16Trans, - pngMetaData.LuminanceTrans); + pngMetaData.Luminance16Trans.GetValueOrDefault(), + pngMetaData.LuminanceTrans.GetValueOrDefault()); break; @@ -690,8 +690,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerPixel, this.bytesPerSample, pngMetaData.HasTrans, - pngMetaData.Rgb48Trans, - pngMetaData.Rgb24Trans); + pngMetaData.Rgb48Trans.GetValueOrDefault(), + pngMetaData.Rgb24Trans.GetValueOrDefault()); break; @@ -740,8 +740,8 @@ namespace SixLabors.ImageSharp.Formats.Png pixelOffset, increment, pngMetaData.HasTrans, - pngMetaData.Luminance16Trans, - pngMetaData.LuminanceTrans); + pngMetaData.Luminance16Trans.GetValueOrDefault(), + pngMetaData.LuminanceTrans.GetValueOrDefault()); break; @@ -779,8 +779,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerPixel, this.bytesPerSample, pngMetaData.HasTrans, - pngMetaData.Rgb48Trans, - pngMetaData.Rgb24Trans); + pngMetaData.Rgb48Trans.GetValueOrDefault(), + pngMetaData.Rgb24Trans.GetValueOrDefault()); break; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 411d5d69b..c76dc308b 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -292,7 +292,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (pngMetaData.HasTrans) { - //Write transparency header + this.WriteTransparencyMarkers(stream, pngMetaData); } this.WritePhysicalChunk(stream, metaData); @@ -305,6 +305,50 @@ namespace SixLabors.ImageSharp.Formats.Png quantized?.Dispose(); } + /// + /// Writes the transparency markers to the stream + /// + /// The containing image data. + /// The image meta data. + private void WriteTransparencyMarkers(Stream stream, PngMetaData pngMetaData) + { + if (pngMetaData.ColorType == PngColorType.Rgb) + { + if (pngMetaData.Rgb48Trans != null) + { + var r = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.R); + var g = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.R); + var B = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.B); + + var alphaArray = r.Concat(g).Concat(B).ToArray(); + + this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); + } + else if (pngMetaData.Rgb24Trans != null) + { + var alphaArray = new byte[6]; + alphaArray[0] = pngMetaData.Rgb24Trans.Value.R; + alphaArray[2] = pngMetaData.Rgb24Trans.Value.G; + alphaArray[4] = pngMetaData.Rgb24Trans.Value.B; + this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); + } + } + else if (pngMetaData.ColorType == PngColorType.Grayscale) + { + if (pngMetaData.Luminance16Trans != null) + { + var alphaArray = BitConverter.GetBytes(pngMetaData.Luminance16Trans.Value); + + this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); + } + else if (pngMetaData.LuminanceTrans != null) + { + var alphaArray = new byte[2]; + alphaArray[0] = pngMetaData.LuminanceTrans.Value; + } + } + } + /// public void Dispose() { @@ -848,7 +892,10 @@ namespace SixLabors.ImageSharp.Formats.Png /// Writes the chunk end to the stream. /// /// The containing image data. - private void WriteEndChunk(Stream stream) => this.WriteChunk(stream, PngChunkType.End, null); + private void WriteEndChunk(Stream stream) + { + this.WriteChunk(stream, PngChunkType.End, null); + } /// /// Writes a chunk to the stream. @@ -856,7 +903,10 @@ namespace SixLabors.ImageSharp.Formats.Png /// The to write to. /// The type of chunk to write. /// The containing data. - private void WriteChunk(Stream stream, PngChunkType type, byte[] data) => this.WriteChunk(stream, type, data, 0, data?.Length ?? 0); + private void WriteChunk(Stream stream, PngChunkType type, byte[] data) + { + this.WriteChunk(stream, type, data, 0, data?.Length ?? 0); + } /// /// Writes a chunk of a specified length to the stream at the given offset. diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs index 0014defbb..765222795 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetaData.cs @@ -47,22 +47,22 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent /// - public Rgb24 Rgb24Trans { get; set; } + public Rgb24? Rgb24Trans { get; set; } /// /// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent /// - public Rgb48 Rgb48Trans { get; set; } + public Rgb48? Rgb48Trans { get; set; } /// /// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent /// - public byte LuminanceTrans { get; set; } + public byte? LuminanceTrans { get; set; } /// /// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent /// - public ushort Luminance16Trans { get; set; } + public ushort? Luminance16Trans { get; set; } /// /// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded From fae06caff766697f82807aed30b4c5fe623b2b39 Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 31 Oct 2018 01:27:11 +0100 Subject: [PATCH 3/9] Fixed bug, code now actually fixed my unit test --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index c76dc308b..edf6cecd7 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -327,9 +327,9 @@ namespace SixLabors.ImageSharp.Formats.Png else if (pngMetaData.Rgb24Trans != null) { var alphaArray = new byte[6]; - alphaArray[0] = pngMetaData.Rgb24Trans.Value.R; - alphaArray[2] = pngMetaData.Rgb24Trans.Value.G; - alphaArray[4] = pngMetaData.Rgb24Trans.Value.B; + alphaArray[1] = pngMetaData.Rgb24Trans.Value.R; + alphaArray[3] = pngMetaData.Rgb24Trans.Value.G; + alphaArray[5] = pngMetaData.Rgb24Trans.Value.B; this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } } @@ -344,7 +344,9 @@ namespace SixLabors.ImageSharp.Formats.Png else if (pngMetaData.LuminanceTrans != null) { var alphaArray = new byte[2]; - alphaArray[0] = pngMetaData.LuminanceTrans.Value; + alphaArray[1] = pngMetaData.LuminanceTrans.Value; + + this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } } } From 02a8d0ddc185979175a4af170bc43e88bc493807 Mon Sep 17 00:00:00 2001 From: Devedse <2350015+devedse@users.noreply.github.com> Date: Wed, 31 Oct 2018 02:04:59 +0100 Subject: [PATCH 4/9] Update PngEncoderCore.cs Lowercase b --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index edf6cecd7..1179a6db3 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -318,9 +318,9 @@ namespace SixLabors.ImageSharp.Formats.Png { var r = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.R); var g = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.R); - var B = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.B); + var b = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.B); - var alphaArray = r.Concat(g).Concat(B).ToArray(); + var alphaArray = r.Concat(g).Concat(b).ToArray(); this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } @@ -1004,4 +1004,4 @@ namespace SixLabors.ImageSharp.Formats.Png return scanlineLength / mod; } } -} \ No newline at end of file +} From a7aef83dcceb7d291b7bed81277e675be2d65ca7 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 2 Nov 2018 17:36:38 +0100 Subject: [PATCH 5/9] Removed unused using --- src/ImageSharp/MetaData/ImageMetaData.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index ec9037479..73549d98a 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.MetaData { From 6364b502da9aed140aa8430ccdddb148acf6efd3 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 2 Nov 2018 17:49:35 +0100 Subject: [PATCH 6/9] Resolved findings in PR --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 24 +++++++++---------- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 24 +++++++++---------- src/ImageSharp/Formats/Png/PngMetaData.cs | 8 +++---- .../Formats/Png/PngScanlineProcessor.cs | 16 ++++++------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 87c22a2ad..c446184d8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -656,8 +656,8 @@ namespace SixLabors.ImageSharp.Formats.Png scanlineSpan, rowSpan, pngMetaData.HasTrans, - pngMetaData.Luminance16Trans.GetValueOrDefault(), - pngMetaData.LuminanceTrans.GetValueOrDefault()); + pngMetaData.TransparentGray16.GetValueOrDefault(), + pngMetaData.TransparentGray8.GetValueOrDefault()); break; @@ -690,8 +690,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerPixel, this.bytesPerSample, pngMetaData.HasTrans, - pngMetaData.Rgb48Trans.GetValueOrDefault(), - pngMetaData.Rgb24Trans.GetValueOrDefault()); + pngMetaData.TransparentRgb48.GetValueOrDefault(), + pngMetaData.TransparentRgb24.GetValueOrDefault()); break; @@ -740,8 +740,8 @@ namespace SixLabors.ImageSharp.Formats.Png pixelOffset, increment, pngMetaData.HasTrans, - pngMetaData.Luminance16Trans.GetValueOrDefault(), - pngMetaData.LuminanceTrans.GetValueOrDefault()); + pngMetaData.TransparentGray16.GetValueOrDefault(), + pngMetaData.TransparentGray8.GetValueOrDefault()); break; @@ -779,8 +779,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerPixel, this.bytesPerSample, pngMetaData.HasTrans, - pngMetaData.Rgb48Trans.GetValueOrDefault(), - pngMetaData.Rgb24Trans.GetValueOrDefault()); + pngMetaData.TransparentRgb48.GetValueOrDefault(), + pngMetaData.TransparentRgb24.GetValueOrDefault()); break; @@ -817,7 +817,7 @@ namespace SixLabors.ImageSharp.Formats.Png ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); - pngMetaData.Rgb48Trans = new Rgb48(rc, gc, bc); + pngMetaData.TransparentRgb48 = new Rgb48(rc, gc, bc); pngMetaData.HasTrans = true; return; } @@ -825,7 +825,7 @@ namespace SixLabors.ImageSharp.Formats.Png byte r = ReadByteLittleEndian(alpha, 0); byte g = ReadByteLittleEndian(alpha, 2); byte b = ReadByteLittleEndian(alpha, 4); - pngMetaData.Rgb24Trans = new Rgb24(r, g, b); + pngMetaData.TransparentRgb24 = new Rgb24(r, g, b); pngMetaData.HasTrans = true; } } @@ -835,11 +835,11 @@ namespace SixLabors.ImageSharp.Formats.Png { if (this.header.BitDepth == 16) { - pngMetaData.Luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); + pngMetaData.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); } else { - pngMetaData.LuminanceTrans = ReadByteLittleEndian(alpha, 0); + pngMetaData.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0)); } pngMetaData.HasTrans = true; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 1179a6db3..e953bc1f0 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -314,37 +314,37 @@ namespace SixLabors.ImageSharp.Formats.Png { if (pngMetaData.ColorType == PngColorType.Rgb) { - if (pngMetaData.Rgb48Trans != null) + if (pngMetaData.TransparentRgb48 != null) { - var r = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.R); - var g = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.R); - var b = BitConverter.GetBytes(pngMetaData.Rgb48Trans.Value.B); + var r = BitConverter.GetBytes(pngMetaData.TransparentRgb48.Value.R); + var g = BitConverter.GetBytes(pngMetaData.TransparentRgb48.Value.R); + var b = BitConverter.GetBytes(pngMetaData.TransparentRgb48.Value.B); var alphaArray = r.Concat(g).Concat(b).ToArray(); this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } - else if (pngMetaData.Rgb24Trans != null) + else if (pngMetaData.TransparentRgb24 != null) { var alphaArray = new byte[6]; - alphaArray[1] = pngMetaData.Rgb24Trans.Value.R; - alphaArray[3] = pngMetaData.Rgb24Trans.Value.G; - alphaArray[5] = pngMetaData.Rgb24Trans.Value.B; + alphaArray[1] = pngMetaData.TransparentRgb24.Value.R; + alphaArray[3] = pngMetaData.TransparentRgb24.Value.G; + alphaArray[5] = pngMetaData.TransparentRgb24.Value.B; this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } } else if (pngMetaData.ColorType == PngColorType.Grayscale) { - if (pngMetaData.Luminance16Trans != null) + if (pngMetaData.TransparentGray16 != null) { - var alphaArray = BitConverter.GetBytes(pngMetaData.Luminance16Trans.Value); + var alphaArray = BitConverter.GetBytes(pngMetaData.TransparentGray16.Value.PackedValue); this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } - else if (pngMetaData.LuminanceTrans != null) + else if (pngMetaData.TransparentGray8 != null) { var alphaArray = new byte[2]; - alphaArray[1] = pngMetaData.LuminanceTrans.Value; + alphaArray[1] = pngMetaData.TransparentGray8.Value.PackedValue; this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); } diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs index 765222795..6a293f770 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetaData.cs @@ -47,22 +47,22 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent /// - public Rgb24? Rgb24Trans { get; set; } + public Rgb24? TransparentRgb24 { get; set; } /// /// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent /// - public Rgb48? Rgb48Trans { get; set; } + public Rgb48? TransparentRgb48 { get; set; } /// /// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent /// - public byte? LuminanceTrans { get; set; } + public Gray8? TransparentGray8 { get; set; } /// /// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent /// - public ushort? Luminance16Trans { get; set; } + public Gray16? TransparentGray16 { get; set; } /// /// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index 3fe590ee2..528c012c5 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png ReadOnlySpan scanlineSpan, Span rowSpan, bool hasTrans, - ushort luminance16Trans, - byte luminanceTrans) + Gray16 luminance16Trans, + Gray8 luminanceTrans) where TPixel : struct, IPixel { TPixel pixel = default; @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Png rgba64.R = luminance; rgba64.G = luminance; rgba64.B = luminance; - rgba64.A = luminance.Equals(luminance16Trans) ? ushort.MinValue : ushort.MaxValue; + rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; pixel.FromRgba64(rgba64); Unsafe.Add(ref rowSpanRef, x) = pixel; @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Png } else { - byte scaledLuminanceTrans = (byte)(luminanceTrans * scaleFactor); + byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); Rgba32 rgba32 = default; for (int x = 0; x < header.Width; x++) { @@ -93,8 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Png int pixelOffset, int increment, bool hasTrans, - ushort luminance16Trans, - byte luminanceTrans) + Gray16 luminance16Trans, + Gray8 luminanceTrans) where TPixel : struct, IPixel { TPixel pixel = default; @@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.Formats.Png rgba64.R = luminance; rgba64.G = luminance; rgba64.B = luminance; - rgba64.A = luminance.Equals(luminance16Trans) ? ushort.MinValue : ushort.MaxValue; + rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; pixel.FromRgba64(rgba64); Unsafe.Add(ref rowSpanRef, x) = pixel; @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Formats.Png } else { - byte scaledLuminanceTrans = (byte)(luminanceTrans * scaleFactor); + byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); Rgba32 rgba32 = default; for (int x = pixelOffset; x < header.Width; x += increment) { From f79f1894a342dae48629c5ea756185f7572af313 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 22 Nov 2018 17:09:48 +0000 Subject: [PATCH 7/9] Reduce allocatoins, check bit depth and rename. --- src/ImageSharp/Formats/Png/PngChunkType.cs | 4 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 6 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 107 ++++++++---------- .../Formats/Png/PngChunkTypeTests.cs | 2 +- .../Formats/Png/PngDecoderTests.Chunks.cs | 2 +- 5 files changed, 56 insertions(+), 65 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngChunkType.cs b/src/ImageSharp/Formats/Png/PngChunkType.cs index 7654c1701..1b251a574 100644 --- a/src/ImageSharp/Formats/Png/PngChunkType.cs +++ b/src/ImageSharp/Formats/Png/PngChunkType.cs @@ -56,10 +56,10 @@ namespace SixLabors.ImageSharp.Formats.Png Text = 0x74455874U, /// - /// This chunk specifies that the image uses simple transparency: + /// The tRNS chunk specifies that the image uses simple transparency: /// either alpha values associated with palette entries (for indexed-color images) /// or a single transparent color (for grayscale and true color images). /// - PaletteAlpha = 0x74524E53U + Transparency = 0x74524E53U } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index c446184d8..9ffac5e62 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Png Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); this.palette = pal; break; - case PngChunkType.PaletteAlpha: + case PngChunkType.Transparency: byte[] alpha = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); this.paletteAlpha = alpha; @@ -306,9 +306,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ReadByteLittleEndian(ReadOnlySpan buffer, int offset) - { - return (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF)); - } + => (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF)); /// /// Attempts to convert a byte array to a new array where each value in the original array is represented by the diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 532e6f747..8a31aaf51 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -292,7 +292,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (pngMetaData.HasTrans) { - this.WriteTransparencyMarkers(stream, pngMetaData); + this.WriteTransparencyChunk(stream, pngMetaData); } this.WritePhysicalChunk(stream, metaData); @@ -305,52 +305,6 @@ namespace SixLabors.ImageSharp.Formats.Png quantized?.Dispose(); } - /// - /// Writes the transparency markers to the stream - /// - /// The containing image data. - /// The image meta data. - private void WriteTransparencyMarkers(Stream stream, PngMetaData pngMetaData) - { - if (pngMetaData.ColorType == PngColorType.Rgb) - { - if (pngMetaData.TransparentRgb48 != null) - { - var r = BitConverter.GetBytes(pngMetaData.TransparentRgb48.Value.R); - var g = BitConverter.GetBytes(pngMetaData.TransparentRgb48.Value.R); - var b = BitConverter.GetBytes(pngMetaData.TransparentRgb48.Value.B); - - var alphaArray = r.Concat(g).Concat(b).ToArray(); - - this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); - } - else if (pngMetaData.TransparentRgb24 != null) - { - var alphaArray = new byte[6]; - alphaArray[1] = pngMetaData.TransparentRgb24.Value.R; - alphaArray[3] = pngMetaData.TransparentRgb24.Value.G; - alphaArray[5] = pngMetaData.TransparentRgb24.Value.B; - this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); - } - } - else if (pngMetaData.ColorType == PngColorType.Grayscale) - { - if (pngMetaData.TransparentGray16 != null) - { - var alphaArray = BitConverter.GetBytes(pngMetaData.TransparentGray16.Value.PackedValue); - - this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); - } - else if (pngMetaData.TransparentGray8 != null) - { - var alphaArray = new byte[2]; - alphaArray[1] = pngMetaData.TransparentGray8.Value.PackedValue; - - this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaArray, 0, alphaArray.Length); - } - } - } - /// public void Dispose() { @@ -377,7 +331,6 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.pngColorType.Equals(PngColorType.Grayscale)) { - // TODO: Research and add support for grayscale plus tRNS if (this.use16Bit) { // 16 bit grayscale @@ -752,7 +705,7 @@ namespace SixLabors.ImageSharp.Formats.Png // Write the transparency data if (anyAlpha) { - this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaTable.Array, 0, paletteLength); + this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength); } } } @@ -800,6 +753,52 @@ namespace SixLabors.ImageSharp.Formats.Png } } + /// + /// Writes the transparency chunk to the stream + /// + /// The containing image data. + /// The image meta data. + private void WriteTransparencyChunk(Stream stream, PngMetaData pngMetaData) + { + if (pngMetaData.ColorType.Equals(PngColorType.Rgb)) + { + Span alpha = this.buffer.AsSpan(); + if (pngMetaData.TransparentRgb48.HasValue && this.use16Bit) + { + Rgb48 rgb = pngMetaData.TransparentRgb48.Value; + BinaryPrimitives.WriteUInt16LittleEndian(alpha, rgb.R); + BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G); + BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B); + + this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 6); + } + else if (pngMetaData.TransparentRgb24.HasValue) + { + alpha.Clear(); + Rgb24 rgb = pngMetaData.TransparentRgb24.Value; + alpha[1] = rgb.R; + alpha[3] = rgb.G; + alpha[5] = rgb.B; + this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 6); + } + } + else if (pngMetaData.ColorType.Equals(PngColorType.Grayscale)) + { + Span alpha = this.buffer.AsSpan(); + if (pngMetaData.TransparentGray16.HasValue && this.bitDepth == 16) + { + BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetaData.TransparentGray16.Value.PackedValue); + this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 2); + } + else if (pngMetaData.TransparentGray8.HasValue) + { + alpha.Clear(); + alpha[1] = pngMetaData.TransparentGray8.Value.PackedValue; + this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 2); + } + } + } + /// /// Writes the pixel information to the stream. /// @@ -894,10 +893,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// Writes the chunk end to the stream. /// /// The containing image data. - private void WriteEndChunk(Stream stream) - { - this.WriteChunk(stream, PngChunkType.End, null); - } + private void WriteEndChunk(Stream stream) => this.WriteChunk(stream, PngChunkType.End, null); /// /// Writes a chunk to the stream. @@ -905,10 +901,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The to write to. /// The type of chunk to write. /// The containing data. - private void WriteChunk(Stream stream, PngChunkType type, byte[] data) - { - this.WriteChunk(stream, type, data, 0, data?.Length ?? 0); - } + private void WriteChunk(Stream stream, PngChunkType type, byte[] data) => this.WriteChunk(stream, type, data, 0, data?.Length ?? 0); /// /// Writes a chunk of a specified length to the stream at the given offset. diff --git a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs index e4cd06ab1..894d902b7 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Equal(PngChunkType.Palette, GetType("PLTE")); Assert.Equal(PngChunkType.Data, GetType("IDAT")); Assert.Equal(PngChunkType.End, GetType("IEND")); - Assert.Equal(PngChunkType.PaletteAlpha, GetType("tRNS")); + Assert.Equal(PngChunkType.Transparency, GetType("tRNS")); Assert.Equal(PngChunkType.Text, GetType("tEXt")); Assert.Equal(PngChunkType.Gamma, GetType("gAMA")); Assert.Equal(PngChunkType.Physical, GetType("pHYs")); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index 2a7d69616..6a0119f0f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [Theory] [InlineData((uint)PngChunkType.Gamma)] // gAMA - [InlineData((uint)PngChunkType.PaletteAlpha)] // tRNS + [InlineData((uint)PngChunkType.Transparency)] // tRNS [InlineData((uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks. //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType) From ead17fd0179c98b99b3212b9213bcefbf9335e89 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 22 Nov 2018 17:12:43 +0000 Subject: [PATCH 8/9] Use existing field --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 8a31aaf51..bb800c8a4 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -785,7 +785,7 @@ namespace SixLabors.ImageSharp.Formats.Png else if (pngMetaData.ColorType.Equals(PngColorType.Grayscale)) { Span alpha = this.buffer.AsSpan(); - if (pngMetaData.TransparentGray16.HasValue && this.bitDepth == 16) + if (pngMetaData.TransparentGray16.HasValue && this.use16Bit) { BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetaData.TransparentGray16.Value.PackedValue); this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 2); From 68496170654216571d54261d3f7074b8e9194a45 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 23 Nov 2018 12:25:05 +0000 Subject: [PATCH 9/9] Fix trns preservation and add tests --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 11 ++- src/ImageSharp/Formats/Png/PngMetaData.cs | 5 ++ .../Formats/Png/PngEncoderTests.cs | 67 ++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 3 + tests/Images/Input/Png/gray-2-tRNS.png | Bin 0 -> 265 bytes tests/Images/Input/Png/gray-4-tRNS.png | Bin 0 -> 267 bytes tests/Images/Input/Png/gray-8-tRNS.png | Bin 0 -> 267 bytes 7 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 tests/Images/Input/Png/gray-2-tRNS.png create mode 100644 tests/Images/Input/Png/gray-4-tRNS.png create mode 100644 tests/Images/Input/Png/gray-8-tRNS.png diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index bb800c8a4..292de007c 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -760,9 +760,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image meta data. private void WriteTransparencyChunk(Stream stream, PngMetaData pngMetaData) { + Span alpha = this.chunkDataBuffer.AsSpan(); if (pngMetaData.ColorType.Equals(PngColorType.Rgb)) { - Span alpha = this.buffer.AsSpan(); if (pngMetaData.TransparentRgb48.HasValue && this.use16Bit) { Rgb48 rgb = pngMetaData.TransparentRgb48.Value; @@ -770,7 +770,7 @@ namespace SixLabors.ImageSharp.Formats.Png BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G); BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B); - this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 6); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); } else if (pngMetaData.TransparentRgb24.HasValue) { @@ -779,22 +779,21 @@ namespace SixLabors.ImageSharp.Formats.Png alpha[1] = rgb.R; alpha[3] = rgb.G; alpha[5] = rgb.B; - this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 6); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); } } else if (pngMetaData.ColorType.Equals(PngColorType.Grayscale)) { - Span alpha = this.buffer.AsSpan(); if (pngMetaData.TransparentGray16.HasValue && this.use16Bit) { BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetaData.TransparentGray16.Value.PackedValue); - this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 2); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } else if (pngMetaData.TransparentGray8.HasValue) { alpha.Clear(); alpha[1] = pngMetaData.TransparentGray8.Value.PackedValue; - this.WriteChunk(stream, PngChunkType.Transparency, this.buffer, 0, 2); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } } } diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs index 6a293f770..d5ab3d255 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetaData.cs @@ -26,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Png this.BitDepth = other.BitDepth; this.ColorType = other.ColorType; this.Gamma = other.Gamma; + this.HasTrans = other.HasTrans; + this.TransparentGray8 = other.TransparentGray8; + this.TransparentGray16 = other.TransparentGray16; + this.TransparentRgb24 = other.TransparentRgb24; + this.TransparentRgb48 = other.TransparentRgb48; } /// diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 5d328db36..9079b15fb 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -25,6 +25,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { TestImages.Png.Bpp1, PngBitDepth.Bit1 } }; + public static readonly TheoryData PngTrnsFiles = + new TheoryData + { + { TestImages.Png.Gray1BitTrans, PngBitDepth.Bit1, PngColorType.Grayscale }, + { TestImages.Png.Gray2BitTrans, PngBitDepth.Bit2, PngColorType.Grayscale }, + { TestImages.Png.Gray4BitTrans, PngBitDepth.Bit4, PngColorType.Grayscale }, + { TestImages.Png.Gray8BitTrans, PngBitDepth.Bit8, PngColorType.Grayscale }, + { TestImages.Png.GrayTrns16BitInterlaced, PngBitDepth.Bit16, PngColorType.Grayscale }, + { TestImages.Png.Rgb24BppTrans, PngBitDepth.Bit8, PngColorType.Rgb }, + { TestImages.Png.Rgb48BppTrans, PngBitDepth.Bit16, PngColorType.Rgb } + }; + /// /// All types except Palette /// @@ -249,6 +261,61 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } + [Theory] + [MemberData(nameof(PngTrnsFiles))] + public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) + { + var options = new PngEncoder(); + + var testFile = TestFile.Create(imagePath); + using (Image input = testFile.CreateImage()) + { + PngMetaData inMeta = input.MetaData.GetFormatMetaData(PngFormat.Instance); + Assert.True(inMeta.HasTrans); + + using (var memStream = new MemoryStream()) + { + input.Save(memStream, options); + memStream.Position = 0; + using (var output = Image.Load(memStream)) + { + PngMetaData outMeta = output.MetaData.GetFormatMetaData(PngFormat.Instance); + Assert.True(outMeta.HasTrans); + + switch (pngColorType) + { + case PngColorType.Grayscale: + if (pngBitDepth.Equals(PngBitDepth.Bit16)) + { + Assert.True(outMeta.TransparentGray16.HasValue); + Assert.Equal(inMeta.TransparentGray16, outMeta.TransparentGray16); + } + else + { + Assert.True(outMeta.TransparentGray8.HasValue); + Assert.Equal(inMeta.TransparentGray8, outMeta.TransparentGray8); + } + + break; + case PngColorType.Rgb: + if (pngBitDepth.Equals(PngBitDepth.Bit16)) + { + Assert.True(outMeta.TransparentRgb48.HasValue); + Assert.Equal(inMeta.TransparentRgb48, outMeta.TransparentRgb48); + } + else + { + Assert.True(outMeta.TransparentRgb24.HasValue); + Assert.Equal(inMeta.TransparentRgb24, outMeta.TransparentRgb24); + } + + break; + } + } + } + } + } + private static void TestPngEncoderCore( TestImageProvider provider, PngColorType pngColorType, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8da458e52..1144a3f7c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -46,6 +46,9 @@ namespace SixLabors.ImageSharp.Tests public const string PDSrc = "Png/pd-source.png"; public const string PDDest = "Png/pd-dest.png"; public const string Gray1BitTrans = "Png/gray-1-trns.png"; + public const string Gray2BitTrans = "Png/gray-2-tRNS.png"; + public const string Gray4BitTrans = "Png/gray-4-tRNS.png"; + public const string Gray8BitTrans = "Png/gray-8-tRNS.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; diff --git a/tests/Images/Input/Png/gray-2-tRNS.png b/tests/Images/Input/Png/gray-2-tRNS.png new file mode 100644 index 0000000000000000000000000000000000000000..8e04cb5020cc1ecf62544cb32d5b79c80db9eb55 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c21SA-yJ@?oEq?k&A{DK)68K?YR2juCLxJHyX z=ND8KWu|A8FsxKCGB7mO0TQMPUile$3eKf@d6{|X8Hu?HPWk0IsYSIV#s7f%N-{$t zN_;YtQ}c>}(hQ6Ysd)?x9M^SweEFIc1X|aBdc-KRJ9m18V9RtbC7#BO-2Ur1W9#j; zlez9mBy!#Etjcre>X=olwIZjW_ui%cf?1oV^|fysu_zijiZ`$`Yrw=Fz( zYoAeHvWEEVQdfX$D4y)I0_Tj_bNTzI@FJ0MksZvXY1vGw-a z$z1m&61i@7R^_>KbOT9H%Gd+$_Bo*Zy%#K^eU$*(yeWi!*+ZLX? zwa=(8SwnpGsmmvLjg7RLZp>J#2DG2Y)5S4_<9c#R2+&W=ml!^t%;`7}6ld^s^>bP0 Hl+XkKeMDTy literal 0 HcmV?d00001 diff --git a/tests/Images/Input/Png/gray-8-tRNS.png b/tests/Images/Input/Png/gray-8-tRNS.png new file mode 100644 index 0000000000000000000000000000000000000000..842245f1d9f192857edd85f43f2d8d6ca8874c01 GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K57&$`wkAjMP?KCsyuhDj#;%@D{=~Y?_KIIn6-IYU;E~gq@uio*>TJG%QoM-uk`SJ+ro3V z_8IjhYlzQ2b@>FZv5{8OjTvjzfcEovx;TbNTux4rdC0)f)WpbSSo>uLP@KWj)z4*} HQ$iB}V~<+M literal 0 HcmV?d00001