From a0dd79cfed664f1ce38a8f713db1304aa3d2b442 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 8 Nov 2016 16:50:54 +1100 Subject: [PATCH] Reduce allocations per filtered row --- .../Formats/Png/Filters/AverageFilter.cs | 7 ++-- .../Formats/Png/Filters/PaethFilter.cs | 7 ++-- .../Formats/Png/Filters/SubFilter.cs | 7 ++-- .../Formats/Png/Filters/UpFilter.cs | 7 ++-- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 42 ++++++++----------- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 97ded289c4..bef124541c 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -45,15 +45,16 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The bytes per pixel. + /// The number of bytes per scanline /// The - public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel) + public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel, int bytesPerScanline) { // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) - byte[] encodedScanline = new byte[scanline.Length + 1]; + byte[] encodedScanline = new byte[bytesPerScanline + 1]; encodedScanline[0] = (byte)FilterType.Average; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) { byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; byte above = previousScanline[x]; diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 1234cb6efe..232d7cc3df 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -45,14 +45,15 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The bytes per pixel. + /// The number of bytes per scanline /// The - public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel) + public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel, int bytesPerScanline) { // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) - byte[] encodedScanline = new byte[scanline.Length + 1]; + byte[] encodedScanline = new byte[bytesPerScanline + 1]; encodedScanline[0] = (byte)FilterType.Paeth; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) { byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; byte above = previousScanline[x]; diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index eb5bd9bfd4..c4fbe3e518 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -38,14 +38,15 @@ namespace ImageSharp.Formats /// /// The scanline to encode /// The bytes per pixel. + /// The number of bytes per scanline /// The - public static byte[] Encode(byte[] scanline, int bytesPerPixel) + public static byte[] Encode(byte[] scanline, int bytesPerPixel, int bytesPerScanline) { // Sub(x) = Raw(x) - Raw(x-bpp) - byte[] encodedScanline = new byte[scanline.Length + 1]; + byte[] encodedScanline = new byte[bytesPerScanline + 1]; encodedScanline[0] = (byte)FilterType.Sub; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) { byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index ebb5e3b54c..026070421b 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -37,15 +37,16 @@ namespace ImageSharp.Formats /// Encodes the scanline /// /// The scanline to encode + /// The number of bytes per scanline /// The previous scanline. /// The - public static byte[] Encode(byte[] scanline, byte[] previousScanline) + public static byte[] Encode(byte[] scanline, int bytesPerScanline, byte[] previousScanline) { // Up(x) = Raw(x) - Prior(x) - byte[] encodedScanline = new byte[scanline.Length + 1]; + byte[] encodedScanline = new byte[bytesPerScanline + 1]; encodedScanline[0] = (byte)FilterType.Up; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) { byte above = previousScanline[x]; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 05fd4c4409..9d68ba84a4 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -331,18 +331,24 @@ namespace ImageSharp.Formats /// The private byte[] EncodePixelData() { - // TODO: Use pointers byte[][] filteredScanlines = new byte[this.height][]; - byte[] previousScanline = new byte[this.width * this.bytesPerPixel]; + int bytesPerScanline = this.width * this.bytesPerPixel; int length = 0; + + byte[] previousScanline = new byte[bytesPerScanline]; + byte[] rawScanline = new byte[bytesPerScanline]; + for (int y = 0; y < this.height; y++) { - byte[] rawScanline = this.GetRawScanline(y); - byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, this.bytesPerPixel); + Buffer.BlockCopy(this.pixelData, y * bytesPerScanline, rawScanline, 0, bytesPerScanline); + byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel); length += filteredScanline.Length; filteredScanlines[y] = filteredScanline; - previousScanline = rawScanline; + // Do a bit of shuffling; + byte[] tmp = rawScanline; + rawScanline = previousScanline; + previousScanline = tmp; } // Flatten the jagged array @@ -362,22 +368,23 @@ namespace ImageSharp.Formats /// /// The raw scanline /// The previous scanline - /// The number of bytes per pixel + /// The number of bytes per scanline + /// The number of bytes per pixel /// The - private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int byteCount) + private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) { Tuple[] candidates = new Tuple[4]; - byte[] sub = SubFilter.Encode(rawScanline, byteCount); + byte[] sub = SubFilter.Encode(rawScanline, bytesPerPixel, bytesPerScanline); candidates[0] = new Tuple(sub, this.CalculateTotalVariation(sub)); - byte[] up = UpFilter.Encode(rawScanline, previousScanline); + byte[] up = UpFilter.Encode(rawScanline, bytesPerScanline, previousScanline); candidates[1] = new Tuple(up, this.CalculateTotalVariation(up)); - byte[] average = AverageFilter.Encode(rawScanline, previousScanline, byteCount); + byte[] average = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline); candidates[2] = new Tuple(average, this.CalculateTotalVariation(average)); - byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, byteCount); + byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline); candidates[3] = new Tuple(paeth, this.CalculateTotalVariation(paeth)); int lowestTotalVariation = int.MaxValue; @@ -413,19 +420,6 @@ namespace ImageSharp.Formats return totalVariation; } - /// - /// Get the raw scanline data from the pixel data - /// - /// The row number - /// The - private byte[] GetRawScanline(int y) - { - int stride = this.bytesPerPixel * this.width; - byte[] rawScanline = new byte[stride]; - Buffer.BlockCopy(this.pixelData, y * stride, rawScanline, 0, stride); - return rawScanline; - } - /// /// Calculates the correct number of bytes per pixel for the given color type. ///