From d61fae265e5d4bd3e0a8038460e09b452799fd81 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 3 Dec 2016 18:22:18 +1100 Subject: [PATCH] A little bit better --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 147 ++++++++++++++++-- tests/ImageSharp.Tests/FileTestBase.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 4 +- .../TestImages/Formats/Png/interlaced.png | 3 + 4 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index fd8972fbbc..dc76c27b82 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -503,7 +503,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]); + this.ProcessInterlacedDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]); Swap(ref scanline, ref previousScanline); } @@ -526,20 +526,17 @@ namespace ImageSharp.Formats /// The de-filtered scanline /// The current image row. /// The image pixels - /// The column start index. Always 0 for none interlaced images. - /// The column increment. Always 1 for none interlaced images. - private void ProcessDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int startIndex = 0, int increment = 1) + private void ProcessDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels) where TColor : struct, IPackedPixel where TPacked : struct { TColor color = default(TColor); - switch (this.PngColorType) { case PngColorType.Grayscale: int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); - for (int x = startIndex; x < this.header.Width; x += increment) + for (int x = 0; x < this.header.Width; x++) { byte intensity = (byte)(newScanline1[x] * factor); color.PackFromBytes(intensity, intensity, intensity, 255); @@ -550,7 +547,7 @@ namespace ImageSharp.Formats case PngColorType.GrayscaleWithAlpha: - for (int x = startIndex; x < this.header.Width; x += increment) + for (int x = 0; x < this.header.Width; x++) { int offset = 1 + (x * this.bytesPerPixel); @@ -571,7 +568,7 @@ namespace ImageSharp.Formats { // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha // channel and we should try to read it. - for (int x = startIndex; x < this.header.Width; x += increment) + for (int x = 0; x < this.header.Width; x++) { int index = newScanline[x]; int pixelOffset = index * 3; @@ -595,7 +592,7 @@ namespace ImageSharp.Formats } else { - for (int x = startIndex; x < this.header.Width; x += increment) + for (int x = 0; x < this.header.Width; x++) { int index = newScanline[x]; int pixelOffset = index * 3; @@ -613,7 +610,7 @@ namespace ImageSharp.Formats case PngColorType.Rgb: - for (int x = startIndex; x < this.header.Width; x += increment) + for (int x = 0; x < this.header.Width; x++) { int offset = 1 + (x * this.bytesPerPixel); @@ -629,7 +626,7 @@ namespace ImageSharp.Formats case PngColorType.RgbWithAlpha: - for (int x = startIndex; x < this.header.Width; x += increment) + for (int x = 0; x < this.header.Width; x++) { int offset = 1 + (x * this.bytesPerPixel); @@ -646,6 +643,129 @@ namespace ImageSharp.Formats } } + /// + /// Processes the interlaced de-filtered scanline filling the image pixel data + /// + /// The pixel format. + /// The packed format. uint, long, float. + /// The de-filtered scanline + /// The current image row. + /// The image pixels + /// The column start index. Always 0 for none interlaced images. + /// The column increment. Always 1 for none interlaced images. + private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1) + where TColor : struct, IPackedPixel + where TPacked : struct + { + TColor color = default(TColor); + + switch (this.PngColorType) + { + case PngColorType.Grayscale: + int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); + byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + for (int x = pixelOffset; x < this.header.Width; x += increment) + { + byte intensity = (byte)(newScanline1[x - pixelOffset] * factor); + color.PackFromBytes(intensity, intensity, intensity, 255); + pixels[x, row] = color; + } + + break; + + case PngColorType.GrayscaleWithAlpha: + + for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + byte intensity = defilteredScanline[o]; + byte alpha = defilteredScanline[o + this.bytesPerSample]; + + color.PackFromBytes(intensity, intensity, intensity, alpha); + pixels[x, row] = color; + } + + break; + + case PngColorType.Palette: + + byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + + if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) + { + // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha + // channel and we should try to read it. + for (int x = pixelOffset; x < this.header.Width; x += increment) + { + int index = newScanline[x - pixelOffset]; + int offset = index * 3; + + byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; + + if (a > 0) + { + byte r = this.palette[offset]; + byte g = this.palette[offset + 1]; + byte b = this.palette[offset + 2]; + color.PackFromBytes(r, g, b, a); + } + else + { + color.PackFromBytes(0, 0, 0, 0); + } + + pixels[x, row] = color; + } + } + else + { + for (int x = pixelOffset; x < this.header.Width; x += increment) + { + int index = newScanline[x - pixelOffset]; + int offset = index * 3; + + byte r = this.palette[offset]; + byte g = this.palette[offset + 1]; + byte b = this.palette[offset + 2]; + + color.PackFromBytes(r, g, b, 255); + pixels[x, row] = color; + } + } + + break; + + case PngColorType.Rgb: + + for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + byte r = defilteredScanline[o]; + byte g = defilteredScanline[o + this.bytesPerSample]; + byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; + + color.PackFromBytes(r, g, b, 255); + pixels[x, row] = color; + } + + break; + + case PngColorType.RgbWithAlpha: + + for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + byte r = defilteredScanline[o]; + byte g = defilteredScanline[o + this.bytesPerSample]; + byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; + byte a = defilteredScanline[o + (3 * this.bytesPerSample)]; + + color.PackFromBytes(r, g, b, a); + pixels[x, row] = color; + } + + break; + } + } + + /// /// Reads a text chunk containing image properties from the data. /// @@ -838,6 +958,11 @@ namespace ImageSharp.Formats chunk.Length = BitConverter.ToInt32(this.chunkLengthBuffer, 0); } + /// + /// Returns the correct number of columns for each interlaced pass. + /// + /// Th current pass index + /// The private int ComputeColumnsAdam7(int pass) { int width = this.header.Width; diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index c2d0916bc2..e8034ebfd1 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Tests // new TestFile(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only // new TestFile(TestImages.Png.Blur), // Perf: Enable for local testing only // new TestFile(TestImages.Png.Indexed), // Perf: Enable for local testing only - new TestFile(TestImages.Png.Splash), + //new TestFile(TestImages.Png.Splash), new TestFile(TestImages.Png.SplashInterlace), // new TestFile(TestImages.Png.Filter0), // Perf: Enable for local testing only // new TestFile(TestImages.Png.Filter1), // Perf: Enable for local testing only diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 23632e2332..a8117df685 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -18,9 +18,9 @@ namespace ImageSharp.Tests public static string Pd => folder + "pd.png"; public static string Blur => folder + "blur.png"; public static string Indexed => folder + "indexed.png"; - public static string Splash => folder + "splash-interlaced.png"; + public static string Splash => folder + "splash.png"; - public static string SplashInterlace => folder + "splash.png"; + public static string SplashInterlace => folder + "splash-interlaced.png"; // filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public static string Filter0 => folder + "filter0.png"; public static string Filter1 => folder + "filter1.png"; diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png new file mode 100644 index 0000000000..d4b75523dd --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e1c92b1a57b762f736e14d480eb016b551ce0dc67aa588b246d838d00c0fc3f +size 17372