From 472350e5b82caff0cb7fb86176b8f4c66a62b65b Mon Sep 17 00:00:00 2001 From: Oleg Bogdanov Date: Thu, 10 Nov 2016 23:22:42 -0800 Subject: [PATCH] Don't allocate memory for png decode filters, reuse scanline buffers - Added png test images with different filters for local tests --- .../Formats/Png/Filters/AverageFilter.cs | 8 +++--- .../Formats/Png/Filters/PaethFilter.cs | 8 +++--- .../Formats/Png/Filters/SubFilter.cs | 9 +++---- .../Formats/Png/Filters/UpFilter.cs | 6 ++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 24 +++++++++--------- tests/ImageSharp.Tests/FileTestBase.cs | 6 +++++ tests/ImageSharp.Tests/TestImages.cs | 9 +++++++ .../TestImages/Formats/Png/filter0.png | Bin 0 -> 2475 bytes .../TestImages/Formats/Png/filter1.png | Bin 0 -> 1180 bytes .../TestImages/Formats/Png/filter2.png | Bin 0 -> 1729 bytes .../TestImages/Formats/Png/filter3.png | Bin 0 -> 1291 bytes .../TestImages/Formats/Png/filter4.png | Bin 0 -> 985 bytes .../TestImages/Formats/Png/filterVar.png | Bin 0 -> 426 bytes 13 files changed, 38 insertions(+), 32 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png 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 0000000000000000000000000000000000000000..d6a1ffff62eb0f1f1dcdb4ba2b1d30473d3adacd GIT binary patch literal 2475 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..26fee958ce7e62a59843205ad34f86a525718595 GIT binary patch literal 1180 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 XLp~IzS7kjt00000NkvXXu0mjfY;#J> literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..758115059d78260f7485521b83a864d4a930be84 GIT binary patch literal 1291 zcmV+m1@!ufP)=&S9{TIAi*MDSax9$V&z4Gt?ul6?wozPIOm-?GqbzHVVL0@-rxKGKhOL8k5O_q zSFpv3t*o}I*V_9Zvg_8{M;@~qp0pb`*{3(#=eF8yFWQ%0wy%ua*LT{9H|_38yXP&t z_Z_?MUHjhq_P_`B;30eDsQu(qd+fM9al)Sb+AZ}0v>o^z(58vARqwd=u@6n@Pmke1XD_3x{N{!lS^S@Mz)_} zdJOa7^M*h)z+aJKlpUap>FUU*babUGt;6i!o}sK%&_Ml_0}Q1o&Mu`O$?Cq66Q@JT z3RxIYM!*w1@Du|XA%l7Q)eI$}3>DD;e|4H6l$4TCgo02|@B=KrQ z@Wkm5Gr226kv_(nB^X7=$b&B_9m1$G>KKWRgkdJ$$Rs9|g_2{8Ez5|#T1H2TVa9h$ z(j;+7%SwO->aXp@3<>@U&;J{qzZIUl8J_zyJbOJn`+IoiYIx>K`1Q0sy`|S$qy4Ql z0v=}L_DlonP=h(73bQba+*O)>Gz;H^EtdX#W}j_9D|OAoJhWjRA~5T>Ndu`v9p<2x zV$3LYKl;cP4%*<5UG=dYJ8U03Vjuo4ycMs0P50HI0S#zE(=pl(GY{sz&C+xa)e;>z z1~aNPzjM$1t-?9LR-*64P#mG-1*D8@fZ(*yqnYop#)_J&`RW&@^-8p-T}(?iQxc|RDc*( z<`4s3eE1|ejyabvT}B)cWe8L&9Y4_UCg9tx=>U_B7``SQ&NPmDBN;&XQlhP3n_*jascABYwWa1geh;>x-+i4(U_Y8$pQBrmp@_v#) zgF2F8aQOzn)E_eOLYJ|y`CQzx3-$nmLD=V&A(sP5g05dja_!i}_ zgIDirr|I{mZhX~}7YtBA6@@6ph=h6QI6&Kt{O(YUr$lPFM|Gd{aIa#L7cd2c1x1(E z(Q=Gs10<^TF-)&&>U0cv)zz`6SBo;LJ%(}H?`NdF>L`))0fxOAVsvH`hUsg)nABw~ zucMi=tL~(v8Fzi|4FCoh!(J`gSItz-2XxH%{{y9tm%W$Hmi_<$002ovPDHLkV1gMt BbC>`C literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3c8b5116e72a63a3de8e0c1892c147071d0269e7 GIT binary patch literal 985 zcmV;~119{5P)hvyJ!8utT1VEc>UN*1Tj!qJPN}IY>N=sRACLdrt)_N=Jy&nmRP0foi1T3q z1vs!^NS8tnTiC(|3Cm|k`D(`N4+4W;;{3xH5HR3=UuuoS4K!$rOa`(R z?R*&X`GvfR1n&u#ACUWVNrjF@+CUA>kh2DD{mP;k>FRtL8*-Oa0-8az@Zi?_D?6u0xgjeiedR*>(yJr`MN%ucekr3 zWi?2)HMNKXi#p_|&$f}^7s7eRdN|{jt0Vw}4uOnAlX>OyYx{;(h32S_hezhCE(Fab znDm|v`WGp9zt{od@3wLWe5y}twl%v9eNEpS4jt=K5jm_s#Yobzz%^iBf5O6mQ4IVB z1gSS#SEq}Q@Y^iry7D|7u$1$9vV7%>V~{k^6C{`$4zP=FjwZm`$rGS zvDv1V?}l%U4QyZyYY4D{53qy=jUGER5T*mg4DDFWy$X8(2FG?szK8L%=^pMOKo~6M z$j~Pc+wCNgX-nw*aZjf2VMtF87IxrAsgQ{`|D+q<6)@}&$53fBJv+1hHpj`E}Y00000NkvXX Hu0mjft5eU! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0b521c1d56ac6ba14d82547eea5c87dae391cfba GIT binary patch literal 426 zcmV;b0agBqP)R*H$WiHScT#vd?>UqJiKVd^cv0p+pyQ zGH|gKM+__s5&6}tHkZ;`DS5Y9+rZhQ zwP@N_D-FoTR{3vH