From 9b4bc472a1fcdb2093d902cccdbca68e1d877aad Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 9 May 2017 13:55:54 +1000 Subject: [PATCH 1/8] Use Unsafe.Add + Bufferspan for Png decode filters --- .../Formats/Png/Filters/AverageFilter.cs | 21 ++++--- .../Formats/Png/Filters/NoneFilter.cs | 12 ---- .../Formats/Png/Filters/PaethFilter.cs | 26 +++++---- .../Formats/Png/Filters/SubFilter.cs | 16 +++-- .../Formats/Png/Filters/UpFilter.cs | 14 ++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 58 +++++++++---------- 6 files changed, 71 insertions(+), 76 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index b4ec49946..31d80c586 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -22,18 +22,23 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(ref byte scanline, ref byte previousScanline, int bytesPerScanline, int bytesPerPixel) { // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) + for (int x = 1; x < bytesPerScanline; x++) { - for (int x = 1; x < bytesPerScanline; x++) + if (x - bytesPerPixel < 1) { - byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - - scan[x] = (byte)((scan[x] + Average(left, above)) % 256); + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + scan = (byte)((scan + (above >> 1)) % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + scan = (byte)((scan + Average(left, above)) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs index 5abd89296..0f67ba5fb 100644 --- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs @@ -15,18 +15,6 @@ namespace ImageSharp.Formats /// internal static class NoneFilter { - /// - /// Decodes the scanline - /// - /// The scanline to decode - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] Decode(byte[] scanline) - { - // No change required. - return scanline; - } - /// /// Encodes the scanline /// diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index a43d4f080..986a291fe 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Formats { - using System; using System.Runtime.CompilerServices; /// @@ -24,19 +23,24 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(ref byte scanline, ref byte previousScanline, int bytesPerScanline, int bytesPerPixel) { // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) + for (int x = 1; x < bytesPerScanline; x++) { - for (int x = 1; x < bytesPerScanline; x++) + if (x - bytesPerPixel < 1) { - byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : prev[x - bytesPerPixel]; - - scan[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256); + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + scan = (byte)((scan + PaethPredicator(0, above, 0)) % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte upperLeft = ref Unsafe.Add(ref previousScanline, x - bytesPerPixel); + scan = (byte)((scan + PaethPredicator(left, above, upperLeft)) % 256); } } } @@ -100,4 +104,4 @@ namespace ImageSharp.Formats return upperLeft; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 4610ed0ae..1220dc5fb 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -21,15 +21,21 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(ref byte scanline, int bytesPerScanline, int bytesPerPixel) { // Sub(x) + Raw(x-bpp) - fixed (byte* scan = scanline) + for (int x = 1; x < bytesPerScanline; x++) { - for (int x = 1; x < bytesPerScanline; x++) + if (x - bytesPerPixel < 1) + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + scan = (byte)(scan % 256); + } + else { - byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; - scan[x] = (byte)((scan[x] + priorRawByte) % 256); + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte prev = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + scan = (byte)((scan + prev) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index 525f50d0f..ee758f64e 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -21,18 +21,14 @@ namespace ImageSharp.Formats /// The previous scanline. /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline) + public static void Decode(ref byte scanline, ref byte previousScanline, int bytesPerScanline) { // Up(x) + Prior(x) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) + for (int x = 1; x < bytesPerScanline; x++) { - for (int x = 1; x < bytesPerScanline; x++) - { - byte above = prev[x]; - - scan[x] = (byte)((scan[x] + above) % 256); - } + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + scan = (byte)((scan + above) % 256); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 67a00efef..ae6fd859e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -184,14 +184,14 @@ namespace ImageSharp.Formats public Image Decode(Stream stream) where TPixel : struct, IPixel { - ImageMetaData metadata = new ImageMetaData(); + var metadata = new ImageMetaData(); this.currentStream = stream; this.currentStream.Skip(8); Image image = null; PixelAccessor pixels = null; try { - using (ZlibInflateStream deframeStream = new ZlibInflateStream(this.currentStream)) + using (var deframeStream = new ZlibInflateStream(this.currentStream)) { PngChunk currentChunk; while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) @@ -437,38 +437,36 @@ namespace ImageSharp.Formats } this.currentRowBytesRead = 0; - FilterType filterType = (FilterType)this.scanline[0]; + + var filterType = (FilterType)this.scanline[0]; + var scanBuffer = new BufferSpan(this.scanline); + ref byte scanPoint = ref scanBuffer.DangerousGetPinnableReference(); + var prevBuffer = new BufferSpan(this.previousScanline); + ref byte prevPoint = ref prevBuffer.DangerousGetPinnableReference(); switch (filterType) { case FilterType.None: - - NoneFilter.Decode(this.scanline); - break; case FilterType.Sub: - SubFilter.Decode(this.scanline, this.bytesPerScanline, this.bytesPerPixel); - + SubFilter.Decode(ref scanPoint, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline); - + UpFilter.Decode(ref scanPoint, ref prevPoint, this.bytesPerScanline); break; case FilterType.Average: - AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline, this.bytesPerPixel); - + AverageFilter.Decode(ref scanPoint, ref prevPoint, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline, this.bytesPerPixel); - + PaethFilter.Decode(ref scanPoint, ref prevPoint, this.bytesPerScanline, this.bytesPerPixel); break; default: @@ -517,38 +515,35 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; - FilterType filterType = (FilterType)this.scanline[0]; + var filterType = (FilterType)this.scanline[0]; + var scanBuffer = new BufferSpan(this.scanline); + ref byte scanPointer = ref scanBuffer.DangerousGetPinnableReference(); + var prevBuffer = new BufferSpan(this.previousScanline); + ref byte prevPointer = ref prevBuffer.DangerousGetPinnableReference(); switch (filterType) { case FilterType.None: - - NoneFilter.Decode(this.scanline); - break; case FilterType.Sub: - SubFilter.Decode(this.scanline, bytesPerInterlaceScanline, this.bytesPerPixel); - + SubFilter.Decode(ref scanPointer, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline); - + UpFilter.Decode(ref scanPointer, ref prevPointer, bytesPerInterlaceScanline); break; case FilterType.Average: - AverageFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); - + AverageFilter.Decode(ref scanPointer, ref prevPointer, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); - + PaethFilter.Decode(ref scanPointer, ref prevPointer, bytesPerInterlaceScanline, this.bytesPerPixel); break; default: @@ -583,9 +578,10 @@ namespace ImageSharp.Formats private void ProcessDefilteredScanline(byte[] defilteredScanline, PixelAccessor pixels) where TPixel : struct, IPixel { - TPixel color = default(TPixel); + var color = default(TPixel); BufferSpan pixelBuffer = pixels.GetRowSpan(this.currentRow); - BufferSpan scanlineBuffer = new BufferSpan(defilteredScanline, 1); + var scanlineBuffer = new BufferSpan(defilteredScanline, 1); + switch (this.PngColorType) { case PngColorType.Grayscale: @@ -646,7 +642,7 @@ namespace ImageSharp.Formats { byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] palette = this.palette; - TPixel color = default(TPixel); + var color = default(TPixel); if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) { @@ -703,7 +699,7 @@ namespace ImageSharp.Formats private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1) where TPixel : struct, IPixel { - TPixel color = default(TPixel); + var color = default(TPixel); switch (this.PngColorType) { @@ -901,7 +897,7 @@ namespace ImageSharp.Formats /// private PngChunk ReadChunk() { - PngChunk chunk = new PngChunk(); + var chunk = new PngChunk(); this.ReadChunkLength(chunk); if (chunk.Length < 0) { From 91626b695ba0eb990c175c2e7ea48e3f1967e04b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 9 May 2017 22:35:43 +1000 Subject: [PATCH 2/8] Use Unsafe.Add + BufferSpan for png encode filters --- .../Formats/Png/Filters/AverageFilter.cs | 29 ++++++---- .../Formats/Png/Filters/PaethFilter.cs | 31 ++++++---- .../Formats/Png/Filters/SubFilter.cs | 25 +++++--- .../Formats/Png/Filters/UpFilter.cs | 21 +++---- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 28 ++++----- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 58 +++++++++++++------ 6 files changed, 115 insertions(+), 77 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 31d80c586..4c2b56e51 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -49,23 +49,30 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. + /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel) + public static void Encode(ref byte scanline, ref byte previousScanline, ref byte result, int bytesPerScanline, int bytesPerPixel) { // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) - fixed (byte* res = result) - { - res[0] = 3; + result = 3; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) + { + if (x - bytesPerPixel < 0) { - byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - - res[x + 1] = (byte)((scan[x] - Average(left, above)) % 256); + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)((scan - (above >> 1)) % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)((scan - Average(left, above)) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 986a291fe..79de59cb9 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -51,24 +51,31 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. + /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel) + public static void Encode(ref byte scanline, ref byte previousScanline, ref byte result, int bytesPerScanline, int bytesPerPixel) { // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) - fixed (byte* res = result) - { - res[0] = 4; + result = 4; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) + { + if (x - bytesPerPixel < 0) { - byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - byte upperLeft = (x - bytesPerPixel < 0) ? (byte)0 : prev[x - bytesPerPixel]; - - res[x + 1] = (byte)((scan[x] - PaethPredicator(left, above, upperLeft)) % 256); + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)((scan - PaethPredicator(0, above, 0)) % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte upperLeft = ref Unsafe.Add(ref previousScanline, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)((scan - PaethPredicator(left, above, upperLeft)) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 1220dc5fb..90d0fa692 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -45,21 +45,28 @@ namespace ImageSharp.Formats /// /// The scanline to encode /// The filtered scanline result. + /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] result, int bytesPerPixel) + public static void Encode(ref byte scanline, ref byte result, int bytesPerScanline, int bytesPerPixel) { // Sub(x) = Raw(x) - Raw(x-bpp) - fixed (byte* scan = scanline) - fixed (byte* res = result) - { - res[0] = 1; + result = 1; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < bytesPerScanline; x++) + { + if (x - bytesPerPixel < 0) { - byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel]; - - res[x + 1] = (byte)((scan[x] - priorRawByte) % 256); + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)(scan % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte prev = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)((scan - prev) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index ee758f64e..fa6733060 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -38,22 +38,19 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. + /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result) + public static void Encode(ref byte scanline, ref byte previousScanline, ref byte result, int bytesPerScanline) { // Up(x) = Raw(x) - Prior(x) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) - fixed (byte* res = result) - { - res[0] = 2; - - for (int x = 0; x < scanline.Length; x++) - { - byte above = prev[x]; + result = 2; - res[x + 1] = (byte)((scan[x] - above) % 256); - } + for (int x = 0; x < bytesPerScanline; x++) + { + ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte res = ref Unsafe.Add(ref result, x + 1); + res = (byte)((scan - above) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index ae6fd859e..f4e1d8261 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -438,11 +438,11 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; - var filterType = (FilterType)this.scanline[0]; - var scanBuffer = new BufferSpan(this.scanline); - ref byte scanPoint = ref scanBuffer.DangerousGetPinnableReference(); - var prevBuffer = new BufferSpan(this.previousScanline); - ref byte prevPoint = ref prevBuffer.DangerousGetPinnableReference(); + var scanSpan = new BufferSpan(this.scanline); + var prevSpan = new BufferSpan(this.previousScanline); + ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); + ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); + var filterType = (FilterType)scanPointer; switch (filterType) { @@ -451,22 +451,22 @@ namespace ImageSharp.Formats case FilterType.Sub: - SubFilter.Decode(ref scanPoint, this.bytesPerScanline, this.bytesPerPixel); + SubFilter.Decode(ref scanPointer, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(ref scanPoint, ref prevPoint, this.bytesPerScanline); + UpFilter.Decode(ref scanPointer, ref prevPointer, this.bytesPerScanline); break; case FilterType.Average: - AverageFilter.Decode(ref scanPoint, ref prevPoint, this.bytesPerScanline, this.bytesPerPixel); + AverageFilter.Decode(ref scanPointer, ref prevPointer, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(ref scanPoint, ref prevPoint, this.bytesPerScanline, this.bytesPerPixel); + PaethFilter.Decode(ref scanPointer, ref prevPointer, this.bytesPerScanline, this.bytesPerPixel); break; default: @@ -515,11 +515,11 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; - var filterType = (FilterType)this.scanline[0]; - var scanBuffer = new BufferSpan(this.scanline); - ref byte scanPointer = ref scanBuffer.DangerousGetPinnableReference(); - var prevBuffer = new BufferSpan(this.previousScanline); - ref byte prevPointer = ref prevBuffer.DangerousGetPinnableReference(); + var scanSpan = new BufferSpan(this.scanline); + var prevSpan = new BufferSpan(this.previousScanline); + ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); + ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); + var filterType = (FilterType)scanPointer; switch (filterType) { diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 31e8cd90e..342067307 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Formats using System.Buffers; using System.IO; using System.Linq; - + using System.Runtime.CompilerServices; using ImageSharp.PixelFormats; using Quantizers; @@ -71,6 +71,11 @@ namespace ImageSharp.Formats /// private int bytesPerPixel; + /// + /// The number of bytes per scanline + /// + private int bytesPerScanline; + /// /// The buffer for the sub filter /// @@ -177,7 +182,7 @@ namespace ImageSharp.Formats this.bytesPerPixel = this.CalculateBytesPerPixel(); - PngHeader header = new PngHeader + var header = new PngHeader { Width = image.Width, Height = image.Height, @@ -307,7 +312,7 @@ namespace ImageSharp.Formats where TPixel : struct, IPixel { // We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory. - using (PixelArea pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) + using (var pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) { pixels.CopyTo(pixelRow, row); } @@ -354,6 +359,11 @@ namespace ImageSharp.Formats /// The private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result) { + var scanSpan = new BufferSpan(rawScanline); + var prevSpan = new BufferSpan(previousScanline); + ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); + ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); + // Palette images don't compress well with adaptive filtering. if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) { @@ -363,13 +373,18 @@ namespace ImageSharp.Formats // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. - UpFilter.Encode(rawScanline, previousScanline, this.up); - int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue); + var upSpan = new BufferSpan(this.up); + ref byte upPointer = ref upSpan.DangerousGetPinnableReference(); + UpFilter.Encode(ref scanPointer, ref prevPointer, ref upPointer, this.bytesPerScanline); + + int currentSum = this.CalculateTotalVariation(ref upPointer, int.MaxValue); int lowestSum = currentSum; result = this.up; - PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(this.paeth, currentSum); + var paethSpan = new BufferSpan(this.paeth); + ref byte paethPointer = ref paethSpan.DangerousGetPinnableReference(); + PaethFilter.Encode(ref scanPointer, ref prevPointer, ref paethPointer, this.bytesPerScanline, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(ref paethPointer, currentSum); if (currentSum < lowestSum) { @@ -377,8 +392,10 @@ namespace ImageSharp.Formats result = this.paeth; } - SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue); + var subSpan = new BufferSpan(this.sub); + ref byte subPointer = ref subSpan.DangerousGetPinnableReference(); + SubFilter.Encode(ref scanPointer, ref subPointer, this.bytesPerScanline, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(ref subPointer, int.MaxValue); if (currentSum < lowestSum) { @@ -386,8 +403,10 @@ namespace ImageSharp.Formats result = this.sub; } - AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(this.average, currentSum); + var averageSpan = new BufferSpan(this.average); + ref byte averagePointer = ref averageSpan.DangerousGetPinnableReference(); + AverageFilter.Encode(ref scanPointer, ref prevPointer, ref averagePointer, this.bytesPerScanline, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(ref averagePointer, currentSum); if (currentSum < lowestSum) { @@ -404,13 +423,14 @@ namespace ImageSharp.Formats /// The scanline bytes /// The last variation sum /// The - private int CalculateTotalVariation(byte[] scanline, int lastSum) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int CalculateTotalVariation(ref byte scanline, int lastSum) { int sum = 0; - for (int i = 1; i < scanline.Length; i++) + for (int i = 1; i < this.bytesPerScanline; i++) { - byte v = scanline[i]; + ref byte v = ref Unsafe.Add(ref scanline, i); sum += v < 128 ? v : 256 - v; // No point continuing if we are larger. @@ -601,10 +621,10 @@ namespace ImageSharp.Formats private void WriteDataChunks(PixelAccessor pixels, Stream stream) where TPixel : struct, IPixel { - int bytesPerScanline = this.width * this.bytesPerPixel; - byte[] previousScanline = new byte[bytesPerScanline]; - byte[] rawScanline = new byte[bytesPerScanline]; - int resultLength = bytesPerScanline + 1; + this.bytesPerScanline = this.width * this.bytesPerPixel; + byte[] previousScanline = new byte[this.bytesPerScanline]; + byte[] rawScanline = new byte[this.bytesPerScanline]; + int resultLength = this.bytesPerScanline + 1; byte[] result = new byte[resultLength]; if (this.pngColorType != PngColorType.Palette) @@ -621,7 +641,7 @@ namespace ImageSharp.Formats try { memoryStream = new MemoryStream(); - using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) + using (var deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) { for (int y = 0; y < this.height; y++) { From 0e9a9ed1f77da10e92fdba32a349c0578c063dde Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 10 May 2017 13:37:29 +1000 Subject: [PATCH 3/8] Simplify API --- .../Formats/Png/Filters/AverageFilter.cs | 37 ++++++++++------- .../Formats/Png/Filters/PaethFilter.cs | 41 +++++++++++-------- .../Formats/Png/Filters/SubFilter.cs | 27 +++++++----- .../Formats/Png/Filters/UpFilter.cs | 23 +++++++---- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 24 +++++------ src/ImageSharp/Formats/Png/PngEncoderCore.cs | 29 ++++++------- 6 files changed, 99 insertions(+), 82 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 4c2b56e51..aabf24b2f 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -22,22 +22,25 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(ref byte scanline, ref byte previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline, int bytesPerPixel) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) for (int x = 1; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 1) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); + byte above = Unsafe.Add(ref prevPointer, x); scan = (byte)((scan + (above >> 1)) % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); - ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); + byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevPointer, x); scan = (byte)((scan + Average(left, above)) % 256); } } @@ -52,26 +55,30 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(ref byte scanline, ref byte previousScanline, ref byte result, int bytesPerScanline, int bytesPerPixel) + public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultPointer = ref result.DangerousGetPinnableReference(); + // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) - result = 3; + resultPointer = 3; for (int x = 0; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 0) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte above = ref Unsafe.Add(ref previousScanline, x); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + byte above = Unsafe.Add(ref prevPointer, x); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)((scan - (above >> 1)) % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); - ref byte above = ref Unsafe.Add(ref previousScanline, x); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevPointer, x); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)((scan - Average(left, above)) % 256); } } diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 79de59cb9..a8e397a30 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -23,23 +23,26 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(ref byte scanline, ref byte previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline, int bytesPerPixel) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) for (int x = 1; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 1) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); + byte above = Unsafe.Add(ref prevPointer, x); scan = (byte)((scan + PaethPredicator(0, above, 0)) % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); - ref byte above = ref Unsafe.Add(ref previousScanline, x); - ref byte upperLeft = ref Unsafe.Add(ref previousScanline, x - bytesPerPixel); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); + byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevPointer, x); + byte upperLeft = Unsafe.Add(ref prevPointer, x - bytesPerPixel); scan = (byte)((scan + PaethPredicator(left, above, upperLeft)) % 256); } } @@ -54,27 +57,31 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(ref byte scanline, ref byte previousScanline, ref byte result, int bytesPerScanline, int bytesPerPixel) + public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultPointer = ref result.DangerousGetPinnableReference(); + // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) - result = 4; + resultPointer = 4; for (int x = 0; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 0) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte above = ref Unsafe.Add(ref previousScanline, x); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + byte above = Unsafe.Add(ref prevPointer, x); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)((scan - PaethPredicator(0, above, 0)) % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte left = ref Unsafe.Add(ref scanline, x - bytesPerPixel); - ref byte above = ref Unsafe.Add(ref previousScanline, x); - ref byte upperLeft = ref Unsafe.Add(ref previousScanline, x - bytesPerPixel); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevPointer, x); + byte upperLeft = Unsafe.Add(ref prevPointer, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)((scan - PaethPredicator(left, above, upperLeft)) % 256); } } diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 90d0fa692..d5ec2e7dc 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -21,20 +21,22 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(ref byte scanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(BufferSpan scanline, int bytesPerScanline, int bytesPerPixel) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + // Sub(x) + Raw(x-bpp) for (int x = 1; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 1) { - ref byte scan = ref Unsafe.Add(ref scanline, x); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); scan = (byte)(scan % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte prev = ref Unsafe.Add(ref scanline, x - bytesPerPixel); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); + byte prev = Unsafe.Add(ref scanPointer, x - bytesPerPixel); scan = (byte)((scan + prev) % 256); } } @@ -48,24 +50,27 @@ namespace ImageSharp.Formats /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(ref byte scanline, ref byte result, int bytesPerScanline, int bytesPerPixel) + public static void Encode(BufferSpan scanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte resultPointer = ref result.DangerousGetPinnableReference(); + // Sub(x) = Raw(x) - Raw(x-bpp) - result = 1; + resultPointer = 1; for (int x = 0; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 0) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)(scan % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte prev = ref Unsafe.Add(ref scanline, x - bytesPerPixel); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + byte prev = Unsafe.Add(ref scanPointer, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)((scan - prev) % 256); } } diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index fa6733060..30cdf65a3 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -21,13 +21,16 @@ namespace ImageSharp.Formats /// The previous scanline. /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(ref byte scanline, ref byte previousScanline, int bytesPerScanline) + public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + // Up(x) + Prior(x) for (int x = 1; x < bytesPerScanline; x++) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte above = ref Unsafe.Add(ref previousScanline, x); + ref byte scan = ref Unsafe.Add(ref scanPointer, x); + byte above = Unsafe.Add(ref prevPointer, x); scan = (byte)((scan + above) % 256); } } @@ -40,16 +43,20 @@ namespace ImageSharp.Formats /// The filtered scanline result. /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(ref byte scanline, ref byte previousScanline, ref byte result, int bytesPerScanline) + public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultPointer = ref result.DangerousGetPinnableReference(); + // Up(x) = Raw(x) - Prior(x) - result = 2; + resultPointer = 2; for (int x = 0; x < bytesPerScanline; x++) { - ref byte scan = ref Unsafe.Add(ref scanline, x); - ref byte above = ref Unsafe.Add(ref previousScanline, x); - ref byte res = ref Unsafe.Add(ref result, x + 1); + byte scan = Unsafe.Add(ref scanPointer, x); + byte above = Unsafe.Add(ref prevPointer, x); + ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); res = (byte)((scan - above) % 256); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index f4e1d8261..b165d0935 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -440,9 +440,7 @@ namespace ImageSharp.Formats var scanSpan = new BufferSpan(this.scanline); var prevSpan = new BufferSpan(this.previousScanline); - ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); - ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); - var filterType = (FilterType)scanPointer; + var filterType = (FilterType)scanSpan[0]; switch (filterType) { @@ -451,22 +449,22 @@ namespace ImageSharp.Formats case FilterType.Sub: - SubFilter.Decode(ref scanPointer, this.bytesPerScanline, this.bytesPerPixel); + SubFilter.Decode(scanSpan, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(ref scanPointer, ref prevPointer, this.bytesPerScanline); + UpFilter.Decode(scanSpan, prevSpan, this.bytesPerScanline); break; case FilterType.Average: - AverageFilter.Decode(ref scanPointer, ref prevPointer, this.bytesPerScanline, this.bytesPerPixel); + AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(ref scanPointer, ref prevPointer, this.bytesPerScanline, this.bytesPerPixel); + PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerScanline, this.bytesPerPixel); break; default: @@ -517,9 +515,7 @@ namespace ImageSharp.Formats var scanSpan = new BufferSpan(this.scanline); var prevSpan = new BufferSpan(this.previousScanline); - ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); - ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); - var filterType = (FilterType)scanPointer; + var filterType = (FilterType)scanSpan[0]; switch (filterType) { @@ -528,22 +524,22 @@ namespace ImageSharp.Formats case FilterType.Sub: - SubFilter.Decode(ref scanPointer, bytesPerInterlaceScanline, this.bytesPerPixel); + SubFilter.Decode(scanSpan, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(ref scanPointer, ref prevPointer, bytesPerInterlaceScanline); + UpFilter.Decode(scanSpan, prevSpan, bytesPerInterlaceScanline); break; case FilterType.Average: - AverageFilter.Decode(ref scanPointer, ref prevPointer, bytesPerInterlaceScanline, this.bytesPerPixel); + AverageFilter.Decode(scanSpan, prevSpan, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(ref scanPointer, ref prevPointer, bytesPerInterlaceScanline, this.bytesPerPixel); + PaethFilter.Decode(scanSpan, prevSpan, bytesPerInterlaceScanline, this.bytesPerPixel); break; default: diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 342067307..2d63539c2 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -361,8 +361,6 @@ namespace ImageSharp.Formats { var scanSpan = new BufferSpan(rawScanline); var prevSpan = new BufferSpan(previousScanline); - ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); - ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); // Palette images don't compress well with adaptive filtering. if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) @@ -374,17 +372,15 @@ namespace ImageSharp.Formats // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. var upSpan = new BufferSpan(this.up); - ref byte upPointer = ref upSpan.DangerousGetPinnableReference(); - UpFilter.Encode(ref scanPointer, ref prevPointer, ref upPointer, this.bytesPerScanline); + UpFilter.Encode(scanSpan, prevSpan, upSpan, this.bytesPerScanline); - int currentSum = this.CalculateTotalVariation(ref upPointer, int.MaxValue); + int currentSum = this.CalculateTotalVariation(upSpan, int.MaxValue); int lowestSum = currentSum; result = this.up; var paethSpan = new BufferSpan(this.paeth); - ref byte paethPointer = ref paethSpan.DangerousGetPinnableReference(); - PaethFilter.Encode(ref scanPointer, ref prevPointer, ref paethPointer, this.bytesPerScanline, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(ref paethPointer, currentSum); + PaethFilter.Encode(scanSpan, prevSpan, paethSpan, this.bytesPerScanline, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(paethSpan, currentSum); if (currentSum < lowestSum) { @@ -393,9 +389,8 @@ namespace ImageSharp.Formats } var subSpan = new BufferSpan(this.sub); - ref byte subPointer = ref subSpan.DangerousGetPinnableReference(); - SubFilter.Encode(ref scanPointer, ref subPointer, this.bytesPerScanline, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(ref subPointer, int.MaxValue); + SubFilter.Encode(scanSpan, subSpan, this.bytesPerScanline, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(subSpan, int.MaxValue); if (currentSum < lowestSum) { @@ -404,9 +399,8 @@ namespace ImageSharp.Formats } var averageSpan = new BufferSpan(this.average); - ref byte averagePointer = ref averageSpan.DangerousGetPinnableReference(); - AverageFilter.Encode(ref scanPointer, ref prevPointer, ref averagePointer, this.bytesPerScanline, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(ref averagePointer, currentSum); + AverageFilter.Encode(scanSpan, prevSpan, averageSpan, this.bytesPerScanline, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(averageSpan, currentSum); if (currentSum < lowestSum) { @@ -424,17 +418,18 @@ namespace ImageSharp.Formats /// The last variation sum /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int CalculateTotalVariation(ref byte scanline, int lastSum) + private int CalculateTotalVariation(BufferSpan scanline, int lastSum) { + ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); int sum = 0; for (int i = 1; i < this.bytesPerScanline; i++) { - ref byte v = ref Unsafe.Add(ref scanline, i); + byte v = Unsafe.Add(ref scanPointer, i); sum += v < 128 ? v : 256 - v; // No point continuing if we are larger. - if (sum > lastSum) + if (sum >= lastSum) { break; } From b5cf29c8d73d9ddee2f5dfbdecadfbd8c62b5139 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 11 May 2017 09:20:52 +1000 Subject: [PATCH 4/8] Rename base refs --- .../Formats/Png/Filters/AverageFilter.cs | 36 ++++++++--------- .../Formats/Png/Filters/PaethFilter.cs | 40 +++++++++---------- .../Formats/Png/Filters/SubFilter.cs | 24 +++++------ .../Formats/Png/Filters/UpFilter.cs | 22 +++++----- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 4 +- 5 files changed, 63 insertions(+), 63 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index aabf24b2f..fcfe698ba 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -24,23 +24,23 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline, int bytesPerPixel) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) for (int x = 1; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 1) { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); - byte above = Unsafe.Add(ref prevPointer, x); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); scan = (byte)((scan + (above >> 1)) % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); - byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); - byte above = Unsafe.Add(ref prevPointer, x); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); scan = (byte)((scan + Average(left, above)) % 256); } } @@ -57,28 +57,28 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultPointer = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) - resultPointer = 3; + resultBaseRef = 3; for (int x = 0; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 0) { - byte scan = Unsafe.Add(ref scanPointer, x); - byte above = Unsafe.Add(ref prevPointer, x); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - (above >> 1)) % 256); } else { - byte scan = Unsafe.Add(ref scanPointer, x); - byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); - byte above = Unsafe.Add(ref prevPointer, x); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - Average(left, above)) % 256); } } diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index a8e397a30..b7b3e8123 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -25,24 +25,24 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline, int bytesPerPixel) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) for (int x = 1; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 1) { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); - byte above = Unsafe.Add(ref prevPointer, x); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); scan = (byte)((scan + PaethPredicator(0, above, 0)) % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); - byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); - byte above = Unsafe.Add(ref prevPointer, x); - byte upperLeft = Unsafe.Add(ref prevPointer, x - bytesPerPixel); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); scan = (byte)((scan + PaethPredicator(left, above, upperLeft)) % 256); } } @@ -59,29 +59,29 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultPointer = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) - resultPointer = 4; + resultBaseRef = 4; for (int x = 0; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 0) { - byte scan = Unsafe.Add(ref scanPointer, x); - byte above = Unsafe.Add(ref prevPointer, x); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - PaethPredicator(0, above, 0)) % 256); } else { - byte scan = Unsafe.Add(ref scanPointer, x); - byte left = Unsafe.Add(ref scanPointer, x - bytesPerPixel); - byte above = Unsafe.Add(ref prevPointer, x); - byte upperLeft = Unsafe.Add(ref prevPointer, x - bytesPerPixel); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - PaethPredicator(left, above, upperLeft)) % 256); } } diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index d5ec2e7dc..6c88f385e 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -23,20 +23,20 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, int bytesPerScanline, int bytesPerPixel) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); // Sub(x) + Raw(x-bpp) for (int x = 1; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 1) { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); scan = (byte)(scan % 256); } else { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); - byte prev = Unsafe.Add(ref scanPointer, x - bytesPerPixel); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); scan = (byte)((scan + prev) % 256); } } @@ -52,25 +52,25 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte resultPointer = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); // Sub(x) = Raw(x) - Raw(x-bpp) - resultPointer = 1; + resultBaseRef = 1; for (int x = 0; x < bytesPerScanline; x++) { if (x - bytesPerPixel < 0) { - byte scan = Unsafe.Add(ref scanPointer, x); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)(scan % 256); } else { - byte scan = Unsafe.Add(ref scanPointer, x); - byte prev = Unsafe.Add(ref scanPointer, x - bytesPerPixel); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - prev) % 256); } } diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index 30cdf65a3..c1969e8cb 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -23,14 +23,14 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Up(x) + Prior(x) for (int x = 1; x < bytesPerScanline; x++) { - ref byte scan = ref Unsafe.Add(ref scanPointer, x); - byte above = Unsafe.Add(ref prevPointer, x); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); scan = (byte)((scan + above) % 256); } } @@ -45,18 +45,18 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); - ref byte prevPointer = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultPointer = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); // Up(x) = Raw(x) - Prior(x) - resultPointer = 2; + resultBaseRef = 2; for (int x = 0; x < bytesPerScanline; x++) { - byte scan = Unsafe.Add(ref scanPointer, x); - byte above = Unsafe.Add(ref prevPointer, x); - ref byte res = ref Unsafe.Add(ref resultPointer, x + 1); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - above) % 256); } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 2d63539c2..bd3cd5fe7 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -420,12 +420,12 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] private int CalculateTotalVariation(BufferSpan scanline, int lastSum) { - ref byte scanPointer = ref scanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); int sum = 0; for (int i = 1; i < this.bytesPerScanline; i++) { - byte v = Unsafe.Add(ref scanPointer, i); + byte v = Unsafe.Add(ref scanBaseRef, i); sum += v < 128 ? v : 256 - v; // No point continuing if we are larger. From cd5de8a694a8259d2fa0ab4e03a5c0f8b4f0940d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 11 May 2017 02:17:01 +0200 Subject: [PATCH 5/8] replaced Png byte[] -s with Buffer --- .../Formats/Png/Filters/NoneFilter.cs | 5 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 35 ++--- src/ImageSharp/Formats/Png/PngEncoder.cs | 6 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 128 +++++++++++------- 4 files changed, 100 insertions(+), 74 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs index 0f67ba5fb..87d794902 100644 --- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs @@ -21,11 +21,12 @@ namespace ImageSharp.Formats /// The scanline to encode /// The filtered scanline result. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] result) + public static void Encode(BufferSpan scanline, BufferSpan result) { // Insert a byte before the data. result[0] = 0; - Buffer.BlockCopy(scanline, 0, result, 1, scanline.Length); + result = result.Slice(1); + BufferSpan.Copy(scanline, result); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index b165d0935..8153d61bb 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -131,12 +131,12 @@ namespace ImageSharp.Formats /// /// Previous scanline processed /// - private byte[] previousScanline; + private Buffer previousScanline; /// /// The current scanline that is being processed /// - private byte[] scanline; + private Buffer scanline; /// /// The index of the current scanline being processed @@ -252,11 +252,8 @@ namespace ImageSharp.Formats finally { pixels?.Dispose(); - if (this.previousScanline != null) - { - ArrayPool.Shared.Return(this.previousScanline); - ArrayPool.Shared.Return(this.scanline); - } + this.scanline?.Dispose(); + this.previousScanline?.Dispose(); } } @@ -345,12 +342,8 @@ namespace ImageSharp.Formats this.bytesPerSample = this.header.BitDepth / 8; } - this.previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline); - this.scanline = ArrayPool.Shared.Rent(this.bytesPerScanline); - - // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero. - Array.Clear(this.scanline, 0, this.bytesPerScanline); - Array.Clear(this.previousScanline, 0, this.bytesPerScanline); + this.previousScanline = Buffer.CreateClean(this.bytesPerScanline); + this.scanline = Buffer.CreateClean(this.bytesPerScanline); } /// @@ -429,7 +422,7 @@ namespace ImageSharp.Formats { while (this.currentRow < this.header.Height) { - int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead); + int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead); this.currentRowBytesRead += bytesRead; if (this.currentRowBytesRead < this.bytesPerScanline) { @@ -438,8 +431,8 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; - var scanSpan = new BufferSpan(this.scanline); - var prevSpan = new BufferSpan(this.previousScanline); + BufferSpan scanSpan = this.scanline.Span; + BufferSpan prevSpan = this.previousScanline.Span; var filterType = (FilterType)scanSpan[0]; switch (filterType) @@ -471,7 +464,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(this.scanline, pixels); + this.ProcessDefilteredScanline(this.scanline.Array, pixels); Swap(ref this.scanline, ref this.previousScanline); this.currentRow++; @@ -504,7 +497,7 @@ namespace ImageSharp.Formats while (this.currentRow < this.header.Height) { - int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead); + int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead); this.currentRowBytesRead += bytesRead; if (this.currentRowBytesRead < bytesPerInterlaceScanline) { @@ -513,8 +506,8 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; - var scanSpan = new BufferSpan(this.scanline); - var prevSpan = new BufferSpan(this.previousScanline); + BufferSpan scanSpan = this.scanline.Span; + BufferSpan prevSpan = this.previousScanline.Span; var filterType = (FilterType)scanSpan[0]; switch (filterType) @@ -546,7 +539,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessInterlacedDefilteredScanline(this.scanline, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); + this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); Swap(ref this.scanline, ref this.previousScanline); diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index e7b6bf30e..f89b624f7 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -33,8 +33,10 @@ namespace ImageSharp.Formats public void Encode(Image image, Stream stream, IPngEncoderOptions options) where TPixel : struct, IPixel { - PngEncoderCore encode = new PngEncoderCore(options); - encode.Encode(image, stream); + using (var encode = new PngEncoderCore(options)) + { + encode.Encode(image, stream); + } } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index bd3cd5fe7..e7ec7d243 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Formats /// /// Performs the png encoding operation. /// - internal sealed class PngEncoderCore + internal sealed class PngEncoderCore : IDisposable { /// /// The maximum block size, defaults at 64k for uncompressed blocks. @@ -72,29 +72,44 @@ namespace ImageSharp.Formats private int bytesPerPixel; /// - /// The number of bytes per scanline + /// The number of bytes per scanline. /// private int bytesPerScanline; + /// + /// The previous scanline. + /// + private Buffer previousScanline; + + /// + /// The raw scanline. + /// + private Buffer rawScanline; + + /// + /// The filtered scanline result. + /// + private Buffer result; + /// /// The buffer for the sub filter /// - private byte[] sub; + private Buffer sub; /// /// The buffer for the up filter /// - private byte[] up; + private Buffer up; /// /// The buffer for the average filter /// - private byte[] average; + private Buffer average; /// /// The buffer for the paeth filter /// - private byte[] paeth; + private Buffer paeth; /// /// The quality of output for images. @@ -212,6 +227,20 @@ namespace ImageSharp.Formats stream.Flush(); } + /// + /// Disposes PngEncoderCore instance, disposing it's internal buffers. + /// + public void Dispose() + { + this.previousScanline?.Dispose(); + this.rawScanline?.Dispose(); + this.result?.Dispose(); + this.sub?.Dispose(); + this.up?.Dispose(); + this.average?.Dispose(); + this.paeth?.Dispose(); + } + /// /// Writes an integer to the byte array. /// @@ -273,10 +302,11 @@ namespace ImageSharp.Formats /// The pixel format. /// The image pixels accessor. /// The row index. - /// The raw scanline. - private void CollectGrayscaleBytes(PixelAccessor pixels, int row, byte[] rawScanline) + private void CollectGrayscaleBytes(PixelAccessor pixels, int row) where TPixel : struct, IPixel { + byte[] rawScanlineArray = this.rawScanline.Array; + // Copy the pixels across from the image. // Reuse the chunk type buffer. for (int x = 0; x < this.width; x++) @@ -291,11 +321,11 @@ namespace ImageSharp.Formats { if (i == 0) { - rawScanline[offset] = luminance; + rawScanlineArray[offset] = luminance; } else { - rawScanline[offset + i] = this.chunkTypeBuffer[3]; + rawScanlineArray[offset + i] = this.chunkTypeBuffer[3]; } } } @@ -307,14 +337,18 @@ namespace ImageSharp.Formats /// The pixel format. /// The image pixel accessor. /// The row index. - /// The raw scanline. - private void CollecTPixelBytes(PixelAccessor pixels, int row, byte[] rawScanline) + private void CollecTPixelBytes(PixelAccessor pixels, int row) where TPixel : struct, IPixel { - // We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory. - using (var pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) + BufferSpan rowSpan = pixels.GetRowSpan(row); + + if (this.bytesPerPixel == 4) + { + PixelOperations.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width); + } + else { - pixels.CopyTo(pixelRow, row); + PixelOperations.Instance.ToXyzBytes(rowSpan, this.rawScanline, this.width); } } @@ -325,89 +359,83 @@ namespace ImageSharp.Formats /// The pixel format. /// The image pixel accessor. /// The row. - /// The previous scanline. - /// The raw scanline. - /// The filtered scanline result. /// The - private byte[] EncodePixelRow(PixelAccessor pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result) + private Buffer EncodePixelRow(PixelAccessor pixels, int row) where TPixel : struct, IPixel { switch (this.pngColorType) { case PngColorType.Palette: - Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length); + Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length, this.rawScanline.Array, 0, this.rawScanline.Length); break; case PngColorType.Grayscale: case PngColorType.GrayscaleWithAlpha: - this.CollectGrayscaleBytes(pixels, row, rawScanline); + this.CollectGrayscaleBytes(pixels, row); break; default: - this.CollecTPixelBytes(pixels, row, rawScanline); + this.CollecTPixelBytes(pixels, row); break; } - return this.GetOptimalFilteredScanline(rawScanline, previousScanline, result); + return this.GetOptimalFilteredScanline(); } /// /// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed /// to be most compressible, using lowest total variation as proxy for compressibility. /// - /// The raw scanline - /// The previous scanline - /// The filtered scanline result. /// The - private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result) + private Buffer GetOptimalFilteredScanline() { - var scanSpan = new BufferSpan(rawScanline); - var prevSpan = new BufferSpan(previousScanline); + BufferSpan scanSpan = this.rawScanline.Span; + BufferSpan prevSpan = this.previousScanline.Span; // Palette images don't compress well with adaptive filtering. if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) { - NoneFilter.Encode(rawScanline, result); - return result; + NoneFilter.Encode(this.rawScanline, this.result); + return this.result; } // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. - var upSpan = new BufferSpan(this.up); + BufferSpan upSpan = this.up.Span; UpFilter.Encode(scanSpan, prevSpan, upSpan, this.bytesPerScanline); int currentSum = this.CalculateTotalVariation(upSpan, int.MaxValue); int lowestSum = currentSum; - result = this.up; + Buffer actualResult = this.up; - var paethSpan = new BufferSpan(this.paeth); + BufferSpan paethSpan = this.paeth.Span; PaethFilter.Encode(scanSpan, prevSpan, paethSpan, this.bytesPerScanline, this.bytesPerPixel); currentSum = this.CalculateTotalVariation(paethSpan, currentSum); if (currentSum < lowestSum) { lowestSum = currentSum; - result = this.paeth; + actualResult = this.paeth; } - var subSpan = new BufferSpan(this.sub); + BufferSpan subSpan = this.sub.Span; SubFilter.Encode(scanSpan, subSpan, this.bytesPerScanline, this.bytesPerPixel); currentSum = this.CalculateTotalVariation(subSpan, int.MaxValue); if (currentSum < lowestSum) { lowestSum = currentSum; - result = this.sub; + actualResult = this.sub; } - var averageSpan = new BufferSpan(this.average); + BufferSpan averageSpan = this.average.Span; AverageFilter.Encode(scanSpan, prevSpan, averageSpan, this.bytesPerScanline, this.bytesPerPixel); currentSum = this.CalculateTotalVariation(averageSpan, currentSum); if (currentSum < lowestSum) { - result = this.average; + actualResult = this.average; } - return result; + return actualResult; } /// @@ -617,17 +645,18 @@ namespace ImageSharp.Formats where TPixel : struct, IPixel { this.bytesPerScanline = this.width * this.bytesPerPixel; - byte[] previousScanline = new byte[this.bytesPerScanline]; - byte[] rawScanline = new byte[this.bytesPerScanline]; int resultLength = this.bytesPerScanline + 1; - byte[] result = new byte[resultLength]; + + this.previousScanline = new Buffer(this.bytesPerScanline); + this.rawScanline = new Buffer(this.bytesPerScanline); + this.result = new Buffer(resultLength); if (this.pngColorType != PngColorType.Palette) { - this.sub = new byte[resultLength]; - this.up = new byte[resultLength]; - this.average = new byte[resultLength]; - this.paeth = new byte[resultLength]; + this.sub = Buffer.CreateClean(resultLength); + this.up = Buffer.CreateClean(resultLength); + this.average = Buffer.CreateClean(resultLength); + this.paeth = Buffer.CreateClean(resultLength); } byte[] buffer; @@ -640,9 +669,10 @@ namespace ImageSharp.Formats { for (int y = 0; y < this.height; y++) { - deflateStream.Write(this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result), 0, resultLength); + Buffer r = this.EncodePixelRow(pixels, y); + deflateStream.Write(r.Array, 0, resultLength); - Swap(ref rawScanline, ref previousScanline); + Swap(ref this.rawScanline, ref this.previousScanline); } } From a2113fab95267f30817474bf5e5c58adab398c5f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 11 May 2017 02:53:04 +0200 Subject: [PATCH 6/8] removed bytesPerScanline parameters, added param checking --- src/ImageSharp/Common/Helpers/Guard.cs | 38 +++++++++++++++++++ .../Formats/Png/Filters/AverageFilter.cs | 17 +++++---- .../Formats/Png/Filters/PaethFilter.cs | 15 +++++--- .../Formats/Png/Filters/SubFilter.cs | 12 +++--- .../Formats/Png/Filters/UpFilter.cs | 15 +++++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 25 ++++++------ src/ImageSharp/Formats/Png/PngEncoderCore.cs | 20 ++++------ 7 files changed, 91 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index cf307e936..41a715af9 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -230,5 +230,43 @@ namespace ImageSharp throw new ArgumentException(message, parameterName); } } + + /// + /// Verifies, that the target span is of same size than the 'other' span. + /// + /// The element type of the spans + /// The target span. + /// The 'other' span to compare 'target' to. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + public static void MustBeSameSized(BufferSpan target, BufferSpan other, string parameterName) + where T : struct + { + if (target.Length != other.Length) + { + throw new ArgumentException("Span-s must be the same size!", parameterName); + } + } + + /// + /// Verifies, that the `target` span has the length of 'minSpan', or longer. + /// + /// The element type of the spans + /// The target span. + /// The 'minSpan' span to compare 'target' to. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + public static void MustBeSizedAtLeast(BufferSpan target, BufferSpan minSpan, string parameterName) + where T : struct + { + if (target.Length < minSpan.Length) + { + throw new ArgumentException($"Span-s must be at least of length {minSpan.Length}!", parameterName); + } + } } } diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index fcfe698ba..80bcb653a 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -12,23 +12,24 @@ namespace ImageSharp.Formats /// the value of a pixel. /// /// - internal static unsafe class AverageFilter + internal static class AverageFilter { /// /// Decodes the scanline /// /// The scanline to decode /// The previous scanline. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerPixel) { + Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) - for (int x = 1; x < bytesPerScanline; x++) + for (int x = 1; x < scanline.Length; x++) { if (x - bytesPerPixel < 1) { @@ -52,11 +53,13 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) + public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerPixel) { + Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); @@ -64,7 +67,7 @@ namespace ImageSharp.Formats // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) resultBaseRef = 3; - for (int x = 0; x < bytesPerScanline; x++) + for (int x = 0; x < scanline.Length; x++) { if (x - bytesPerPixel < 0) { diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index b7b3e8123..9f21f0759 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -20,16 +20,17 @@ namespace ImageSharp.Formats /// /// The scanline to decode /// The previous scanline. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerPixel) { + Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - for (int x = 1; x < bytesPerScanline; x++) + for (int x = 1; x < scanline.Length; x++) { if (x - bytesPerPixel < 1) { @@ -54,11 +55,13 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) + public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerPixel) { + Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); @@ -66,7 +69,7 @@ namespace ImageSharp.Formats // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) resultBaseRef = 4; - for (int x = 0; x < bytesPerScanline; x++) + for (int x = 0; x < scanline.Length; x++) { if (x - bytesPerPixel < 0) { diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 6c88f385e..3a5396cb3 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -18,15 +18,14 @@ namespace ImageSharp.Formats /// Decodes the scanline /// /// The scanline to decode - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(BufferSpan scanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(BufferSpan scanline, int bytesPerPixel) { ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); // Sub(x) + Raw(x-bpp) - for (int x = 1; x < bytesPerScanline; x++) + for (int x = 1; x < scanline.Length; x++) { if (x - bytesPerPixel < 1) { @@ -47,18 +46,19 @@ namespace ImageSharp.Formats /// /// The scanline to encode /// The filtered scanline result. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(BufferSpan scanline, BufferSpan result, int bytesPerScanline, int bytesPerPixel) + public static void Encode(BufferSpan scanline, BufferSpan result, int bytesPerPixel) { + Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); // Sub(x) = Raw(x) - Raw(x-bpp) resultBaseRef = 1; - for (int x = 0; x < bytesPerScanline; x++) + for (int x = 0; x < scanline.Length; x++) { if (x - bytesPerPixel < 0) { diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index c1969e8cb..b30c49c45 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -19,15 +19,16 @@ namespace ImageSharp.Formats /// /// The scanline to decode /// The previous scanline. - /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerScanline) + public static void Decode(BufferSpan scanline, BufferSpan previousScanline) { + Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Up(x) + Prior(x) - for (int x = 1; x < bytesPerScanline; x++) + for (int x = 1; x < scanline.Length; x++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); @@ -41,10 +42,12 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. - /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerScanline) + public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result) { + Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); @@ -52,7 +55,7 @@ namespace ImageSharp.Formats // Up(x) = Raw(x) - Prior(x) resultBaseRef = 2; - for (int x = 0; x < bytesPerScanline; x++) + for (int x = 0; x < scanline.Length; x++) { byte scan = Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 8153d61bb..13b4c1167 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -430,10 +430,7 @@ namespace ImageSharp.Formats } this.currentRowBytesRead = 0; - - BufferSpan scanSpan = this.scanline.Span; - BufferSpan prevSpan = this.previousScanline.Span; - var filterType = (FilterType)scanSpan[0]; + var filterType = (FilterType)this.scanline[0]; switch (filterType) { @@ -442,22 +439,22 @@ namespace ImageSharp.Formats case FilterType.Sub: - SubFilter.Decode(scanSpan, this.bytesPerScanline, this.bytesPerPixel); + SubFilter.Decode(this.scanline, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(scanSpan, prevSpan, this.bytesPerScanline); + UpFilter.Decode(this.scanline, this.previousScanline); break; case FilterType.Average: - AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerScanline, this.bytesPerPixel); + AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerScanline, this.bytesPerPixel); + PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel); break; default: @@ -506,8 +503,8 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; - BufferSpan scanSpan = this.scanline.Span; - BufferSpan prevSpan = this.previousScanline.Span; + BufferSpan scanSpan = this.scanline.Slice(0, bytesPerInterlaceScanline); + BufferSpan prevSpan = this.previousScanline.Span.Slice(0, bytesPerInterlaceScanline); var filterType = (FilterType)scanSpan[0]; switch (filterType) @@ -517,22 +514,22 @@ namespace ImageSharp.Formats case FilterType.Sub: - SubFilter.Decode(scanSpan, bytesPerInterlaceScanline, this.bytesPerPixel); + SubFilter.Decode(scanSpan, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(scanSpan, prevSpan, bytesPerInterlaceScanline); + UpFilter.Decode(scanSpan, prevSpan); break; case FilterType.Average: - AverageFilter.Decode(scanSpan, prevSpan, bytesPerInterlaceScanline, this.bytesPerPixel); + AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(scanSpan, prevSpan, bytesPerInterlaceScanline, this.bytesPerPixel); + PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel); break; default: diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index e7ec7d243..44f1513ee 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -399,16 +399,14 @@ namespace ImageSharp.Formats // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. - BufferSpan upSpan = this.up.Span; - UpFilter.Encode(scanSpan, prevSpan, upSpan, this.bytesPerScanline); + UpFilter.Encode(scanSpan, prevSpan, this.up); - int currentSum = this.CalculateTotalVariation(upSpan, int.MaxValue); + int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue); int lowestSum = currentSum; Buffer actualResult = this.up; - BufferSpan paethSpan = this.paeth.Span; - PaethFilter.Encode(scanSpan, prevSpan, paethSpan, this.bytesPerScanline, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(paethSpan, currentSum); + PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(this.paeth, currentSum); if (currentSum < lowestSum) { @@ -416,9 +414,8 @@ namespace ImageSharp.Formats actualResult = this.paeth; } - BufferSpan subSpan = this.sub.Span; - SubFilter.Encode(scanSpan, subSpan, this.bytesPerScanline, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(subSpan, int.MaxValue); + SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue); if (currentSum < lowestSum) { @@ -426,9 +423,8 @@ namespace ImageSharp.Formats actualResult = this.sub; } - BufferSpan averageSpan = this.average.Span; - AverageFilter.Encode(scanSpan, prevSpan, averageSpan, this.bytesPerScanline, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(averageSpan, currentSum); + AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel); + currentSum = this.CalculateTotalVariation(this.average, currentSum); if (currentSum < lowestSum) { From 4e0737a0c221a13cead5adbf74cac5da15733a40 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 11 May 2017 11:50:23 +1000 Subject: [PATCH 7/8] Use DebugGuard --- src/ImageSharp/Common/Helpers/DebugGuard.cs | 38 +++++++++++++++++++ src/ImageSharp/Common/Helpers/Guard.cs | 38 ------------------- .../Formats/Png/Filters/PaethFilter.cs | 6 +-- .../Formats/Png/Filters/SubFilter.cs | 2 +- .../Formats/Png/Filters/UpFilter.cs | 6 +-- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index c1fa46191..f7c83452f 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -159,5 +159,43 @@ namespace ImageSharp throw new ArgumentException(message, parameterName); } } + + /// + /// Verifies, that the target span is of same size than the 'other' span. + /// + /// The element type of the spans + /// The target span. + /// The 'other' span to compare 'target' to. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + public static void MustBeSameSized(BufferSpan target, BufferSpan other, string parameterName) + where T : struct + { + if (target.Length != other.Length) + { + throw new ArgumentException("Span-s must be the same size!", parameterName); + } + } + + /// + /// Verifies, that the `target` span has the length of 'minSpan', or longer. + /// + /// The element type of the spans + /// The target span. + /// The 'minSpan' span to compare 'target' to. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + public static void MustBeSizedAtLeast(BufferSpan target, BufferSpan minSpan, string parameterName) + where T : struct + { + if (target.Length < minSpan.Length) + { + throw new ArgumentException($"Span-s must be at least of length {minSpan.Length}!", parameterName); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 41a715af9..cf307e936 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -230,43 +230,5 @@ namespace ImageSharp throw new ArgumentException(message, parameterName); } } - - /// - /// Verifies, that the target span is of same size than the 'other' span. - /// - /// The element type of the spans - /// The target span. - /// The 'other' span to compare 'target' to. - /// The name of the parameter that is to be checked. - /// - /// is true - /// - public static void MustBeSameSized(BufferSpan target, BufferSpan other, string parameterName) - where T : struct - { - if (target.Length != other.Length) - { - throw new ArgumentException("Span-s must be the same size!", parameterName); - } - } - - /// - /// Verifies, that the `target` span has the length of 'minSpan', or longer. - /// - /// The element type of the spans - /// The target span. - /// The 'minSpan' span to compare 'target' to. - /// The name of the parameter that is to be checked. - /// - /// is true - /// - public static void MustBeSizedAtLeast(BufferSpan target, BufferSpan minSpan, string parameterName) - where T : struct - { - if (target.Length < minSpan.Length) - { - throw new ArgumentException($"Span-s must be at least of length {minSpan.Length}!", parameterName); - } - } } } diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 9f21f0759..4226596f8 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerPixel) { - Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); @@ -59,8 +59,8 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerPixel) { - Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 3a5396cb3..d40f261ed 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -50,7 +50,7 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan result, int bytesPerPixel) { - Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index b30c49c45..fa3ef57da 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, BufferSpan previousScanline) { - Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); @@ -45,8 +45,8 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result) { - Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); From 1bc7fee3fb8bd262eb58037e72dff76a701cd56d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 11 May 2017 11:51:41 +1000 Subject: [PATCH 8/8] Add missed file --- src/ImageSharp/Formats/Png/Filters/AverageFilter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 80bcb653a..d3b32d977 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -23,7 +23,7 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerPixel) { - Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); @@ -57,8 +57,8 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerPixel) { - Guard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - Guard.MustBeSizedAtLeast(result, scanline, nameof(result)); + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference();