From 9b4bc472a1fcdb2093d902cccdbca68e1d877aad Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 9 May 2017 13:55:54 +1000 Subject: [PATCH] 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) {