diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index cd95eba560..c28e490cbe 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -26,22 +26,20 @@ namespace ImageSharp.Formats public static byte[] Decode(byte[] scanline, byte[] previousScanline, int bytesPerPixel) { // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) - byte[] result = new byte[scanline.Length]; fixed (byte* scan = scanline) fixed (byte* prev = previousScanline) - fixed (byte* res = result) { for (int x = 1; x < scanline.Length; x++) { - byte left = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel]; + byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; byte above = prev[x]; - res[x] = (byte)((scan[x] + Average(left, above)) % 256); + scan[x] = (byte)((scan[x] + Average(left, above)) % 256); } } - return result; + return scanline; } /// diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 16c0378e96..e0f455e002 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -25,23 +25,21 @@ namespace ImageSharp.Formats public static byte[] Decode(byte[] scanline, byte[] previousScanline, int bytesPerPixel) { // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - byte[] result = new byte[scanline.Length]; fixed (byte* scan = scanline) fixed (byte* prev = previousScanline) - fixed (byte* res = result) { for (int x = 1; x < scanline.Length; x++) { - byte left = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel]; + byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; byte above = prev[x]; byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : prev[x - bytesPerPixel]; - res[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256); + scan[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256); } } - return result; + return scanline; } /// diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 63e41a1d55..904dc191d0 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -21,20 +21,17 @@ namespace ImageSharp.Formats public static byte[] Decode(byte[] scanline, int bytesPerPixel) { // Sub(x) + Raw(x-bpp) - byte[] result = new byte[scanline.Length]; fixed (byte* scan = scanline) - fixed (byte* res = result) { for (int x = 1; x < scanline.Length; x++) { - byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel]; - - res[x] = (byte)((scan[x] + priorRawByte) % 256); + byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; + scan[x] = (byte)((scan[x] + priorRawByte) % 256); } } - return result; + return scanline; } /// diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index b40aa944b5..c4572f7a5a 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -21,21 +21,19 @@ namespace ImageSharp.Formats public static byte[] Decode(byte[] scanline, byte[] previousScanline) { // Up(x) + Prior(x) - byte[] result = new byte[scanline.Length]; fixed (byte* scan = scanline) fixed (byte* prev = previousScanline) - fixed (byte* res = result) { for (int x = 1; x < scanline.Length; x++) { byte above = prev[x]; - res[x] = (byte)((scan[x] + above) % 256); + scan[x] = (byte)((scan[x] + above) % 256); } } - return result; + return scanline; } /// diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 186e994372..22062c964c 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Formats using System.IO; using System.Linq; using System.Text; - + /// /// Performs the png decoding operation. /// @@ -276,45 +276,42 @@ namespace ImageSharp.Formats // TODO: ArrayPool.Shared.Rent(this.bytesPerScanline) byte[] previousScanline = new byte[this.bytesPerScanline]; byte[] scanline = new byte[this.bytesPerScanline]; + for (int y = 0; y < this.header.Height; y++) { compressedStream.Read(scanline, 0, this.bytesPerScanline); FilterType filterType = (FilterType)scanline[0]; - byte[] defilteredScanline; - // TODO: It would be good if we can reduce the memory usage here - Each filter is creating a new row. - // Every time I try to use the same approach as I have in the encoder though I keep messing up. - // Fingers crossed someone with a big brain and a kind heart will come along and finish optimizing this for me. switch (filterType) { case FilterType.None: - defilteredScanline = NoneFilter.Decode(scanline); + NoneFilter.Decode(scanline); break; case FilterType.Sub: - defilteredScanline = SubFilter.Decode(scanline, this.bytesPerPixel); + SubFilter.Decode(scanline, this.bytesPerPixel); break; case FilterType.Up: - defilteredScanline = UpFilter.Decode(scanline, previousScanline); + UpFilter.Decode(scanline, previousScanline); break; case FilterType.Average: - defilteredScanline = AverageFilter.Decode(scanline, previousScanline, this.bytesPerPixel); + AverageFilter.Decode(scanline, previousScanline, this.bytesPerPixel); break; case FilterType.Paeth: - defilteredScanline = PaethFilter.Decode(scanline, previousScanline, this.bytesPerPixel); + PaethFilter.Decode(scanline, previousScanline, this.bytesPerPixel); break; @@ -322,8 +319,11 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - previousScanline = defilteredScanline; - this.ProcessDefilteredScanline(defilteredScanline, y, pixels); + this.ProcessDefilteredScanline(scanline, y, pixels); + + byte[] temp = previousScanline; + previousScanline = scanline; + scanline = temp; } } diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index b9a226fc8c..6ef94fb6f9 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -32,6 +32,12 @@ namespace ImageSharp.Tests // 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.Filter0), // Perf: Enable for local testing only + // new TestFile(TestImages.Png.Filter1), // Perf: Enable for local testing only + // new TestFile(TestImages.Png.Filter2), // Perf: Enable for local testing only + // new TestFile(TestImages.Png.Filter3), // Perf: Enable for local testing only + // new TestFile(TestImages.Png.Filter4), // Perf: Enable for local testing only + // new TestFile(TestImages.Png.FilterVar), // Perf: Enable for local testing only new TestFile(TestImages.Gif.Rings), // new TestFile(TestImages.Gif.Giphy) // Perf: Enable for local testing only }; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e6d3cd7570..a5eebeebe9 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -19,6 +19,15 @@ namespace ImageSharp.Tests public static string Blur => folder + "blur.png"; public static string Indexed => folder + "indexed.png"; public static string Splash => folder + "splash.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"; + public static string Filter2 => folder + "filter2.png"; + public static string Filter3 => folder + "filter3.png"; + public static string Filter4 => folder + "filter4.png"; + // filter changing per scanline + public static string FilterVar => folder + "filterVar.png"; } public static class Jpeg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png new file mode 100644 index 0000000000..3089caeb86 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fe92b2aa2da04c885d1dbd85c834716f6cdd946364d97dcd597bb79d9e14427 +size 2475 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png new file mode 100644 index 0000000000..b4dd9cf2bb --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef072ec6815ebf9b33e0553d2e4e4e7ed6911860a2512c67bcd10a9f0f09b9de +size 1180 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png new file mode 100644 index 0000000000..2a9416ea67 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca4b937b3c587d5c007f193a2eec14dc96b0d23ff7d6aa9004e3badd1af9fe8f +size 1729 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png new file mode 100644 index 0000000000..a2c4c96aa8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d101e3ef4f78a69437034671e93fe11faac0cfc4d44210dcca1b944caa886f7 +size 1291 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png new file mode 100644 index 0000000000..48c8b03217 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c365c24153cb69fd3c162f00b296ae23a71a1595645d1aeb0ad23af680d7b4be +size 985 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png new file mode 100644 index 0000000000..32c7c08bab --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ac0f095d2a943157e820fa121bccde08d5230af1b5830c3041d5f4da3524eba +size 426