From 89297f314d9cb0d5da9576450a00380e8177c875 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 | Bin 0 -> 17372 bytes 4 files changed, 139 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 0000000000000000000000000000000000000000..ca39e19d8ff6765a11a9bfd1ca11b7b1b5d52d57 GIT binary patch literal 17372 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 z