diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 552673179..469c459fb 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -495,52 +495,54 @@ namespace ImageSharp.Formats // Grab the palette and write it to the stream. TColor[] palette = quantized.Palette; - int pixelCount = palette.Length; - List transparentPixels = new List(); + byte pixelCount = palette.Length.ToByte(); // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); + byte[] alphaTable = ArrayPool.Shared.Rent(pixelCount); byte[] bytes = ArrayPool.Shared.Rent(4); - + bool anyAlpha = false; try { - for (int i = 0; i < pixelCount; i++) + for (byte i = 0; i < pixelCount; i++) { - int offset = i * 3; - palette[i].ToXyzwBytes(bytes, 0); + if (quantized.Pixels.Contains(i)) + { + int offset = i * 3; + palette[i].ToXyzwBytes(bytes, 0); - int alpha = bytes[3]; + byte alpha = bytes[3]; - colorTable[offset] = bytes[0]; - colorTable[offset + 1] = bytes[1]; - colorTable[offset + 2] = bytes[2]; + colorTable[offset] = bytes[0]; + colorTable[offset + 1] = bytes[1]; + colorTable[offset + 2] = bytes[2]; - if (alpha < 255 && alpha <= this.options.Threshold) - { - // Ensure the index is actually being used in our array. - // I'd like to find a faster way of doing this. - if (quantized.Pixels.Contains((byte)i)) + if (alpha > this.options.Threshold) { - transparentPixels.Add((byte)i); + alpha = 255; } + + anyAlpha = anyAlpha || alpha < 255; + alphaTable[i] = alpha; } } this.WriteChunk(stream, PngChunkTypes.Palette, colorTable, 0, colorTableLength); + + // Write the transparency data + if (anyAlpha) + { + this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, alphaTable, 0, pixelCount); + } } finally { ArrayPool.Shared.Return(colorTable); + ArrayPool.Shared.Return(alphaTable); ArrayPool.Shared.Return(bytes); } - // Write the transparency data - if (transparentPixels.Any()) - { - this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, transparentPixels.ToArray()); - } - return quantized; } diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs index 2891f1974..90175c6d6 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs @@ -60,7 +60,7 @@ namespace ImageSharp.Formats /// /// Gets or sets the transparency threshold. /// - public byte Threshold { get; set; } = 0; + public byte Threshold { get; set; } = 255; /// /// Gets or sets a value indicating whether this instance should write diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 882f903d6..2ba570453 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -13,6 +13,7 @@ namespace ImageSharp.Tests.Formats.Png using ImageSharp.Formats; using System.Linq; using ImageSharp.IO; + using System.Numerics; public class PngSmokeTests { @@ -58,6 +59,48 @@ namespace ImageSharp.Tests.Formats.Png } } + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Color)] + public void CanSaveIndexedPngTwice(TestImageProvider provider) + where TColor : struct, IPixel + { + // does saving a file then repoening mean both files are identical??? + using (Image source = provider.GetImage()) + using (MemoryStream ms = new MemoryStream()) + { + source.MetaData.Quality = 256; + source.Save(ms, new PngEncoder(), new PngEncoderOptions { + Threshold = 200 + }); + ms.Position = 0; + using (Image img1 = Image.Load(ms, new PngDecoder())) + { + using (MemoryStream ms2 = new MemoryStream()) + { + img1.Save(ms2, new PngEncoder(), new PngEncoderOptions + { + Threshold = 200 + }); + ms2.Position = 0; + using (Image img2 = Image.Load(ms2, new PngDecoder())) + { + using (PixelAccessor pixels1 = img1.Lock()) + using (PixelAccessor pixels2 = img2.Lock()) + { + for (int y = 0; y < img1.Height; y++) + { + for (int x = 0; x < img1.Width; x++) + { + Assert.Equal(pixels1[x, y], pixels2[x, y]); + } + } + } + } + } + } + } + } + [Theory] [WithTestPatternImages(300, 300, PixelTypes.All)] public void Resize(TestImageProvider provider)