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/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index b4ec49946..d3b32d977 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -12,28 +12,37 @@ 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(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
+ public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerPixel)
{
+ DebugGuard.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)
- fixed (byte* scan = scanline)
- fixed (byte* prev = previousScanline)
+ for (int x = 1; x < scanline.Length; 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 scanBaseRef, x);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ scan = (byte)((scan + (above >> 1)) % 256);
+ }
+ else
+ {
+ 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);
}
}
}
@@ -46,21 +55,34 @@ namespace ImageSharp.Formats
/// The filtered scanline result.
/// The bytes per pixel.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel)
+ public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerPixel)
{
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
+
+ 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)
- fixed (byte* scan = scanline)
- fixed (byte* prev = previousScanline)
- fixed (byte* res = result)
- {
- res[0] = 3;
+ resultBaseRef = 3;
- for (int x = 0; x < scanline.Length; x++)
+ for (int x = 0; x < scanline.Length; 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);
+ 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 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/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
index 5abd89296..87d794902 100644
--- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
@@ -15,29 +15,18 @@ 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
///
/// 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/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index a43d4f080..4226596f8 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;
///
@@ -21,22 +20,31 @@ 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(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
+ public static void Decode(BufferSpan scanline, BufferSpan previousScanline, int bytesPerPixel)
{
+ DebugGuard.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))
- fixed (byte* scan = scanline)
- fixed (byte* prev = previousScanline)
+ for (int x = 1; x < scanline.Length; 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 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 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);
}
}
}
@@ -49,22 +57,35 @@ namespace ImageSharp.Formats
/// The filtered scanline result.
/// The bytes per pixel.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel)
+ public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result, int bytesPerPixel)
{
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
+
+ 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))
- fixed (byte* scan = scanline)
- fixed (byte* prev = previousScanline)
- fixed (byte* res = result)
- {
- res[0] = 4;
+ resultBaseRef = 4;
- for (int x = 0; x < scanline.Length; x++)
+ for (int x = 0; x < scanline.Length; 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);
+ 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 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);
}
}
}
@@ -100,4 +121,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..d40f261ed 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -18,18 +18,25 @@ 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(byte[] scanline, int bytesPerScanline, int bytesPerPixel)
+ public static void Decode(BufferSpan scanline, int bytesPerPixel)
{
+ ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference();
+
// Sub(x) + Raw(x-bpp)
- fixed (byte* scan = scanline)
+ for (int x = 1; x < scanline.Length; x++)
{
- for (int x = 1; x < bytesPerScanline; x++)
+ if (x - bytesPerPixel < 1)
+ {
+ ref byte scan = ref Unsafe.Add(ref scanBaseRef, 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 scanBaseRef, x);
+ byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel);
+ scan = (byte)((scan + prev) % 256);
}
}
}
@@ -41,19 +48,30 @@ namespace ImageSharp.Formats
/// The filtered scanline result.
/// The bytes per pixel.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(byte[] scanline, byte[] result, int bytesPerPixel)
+ public static void Encode(BufferSpan scanline, BufferSpan result, int bytesPerPixel)
{
+ DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
+
+ ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference();
+ ref byte resultBaseRef = ref result.DangerousGetPinnableReference();
+
// Sub(x) = Raw(x) - Raw(x-bpp)
- fixed (byte* scan = scanline)
- fixed (byte* res = result)
- {
- res[0] = 1;
+ resultBaseRef = 1;
- for (int x = 0; x < scanline.Length; x++)
+ for (int x = 0; x < scanline.Length; x++)
+ {
+ if (x - bytesPerPixel < 0)
{
- byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
-
- res[x + 1] = (byte)((scan[x] - priorRawByte) % 256);
+ 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 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 525f50d0f..fa3ef57da 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -19,20 +19,20 @@ namespace ImageSharp.Formats
///
/// The scanline to decode
/// 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(BufferSpan scanline, BufferSpan previousScanline)
{
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+
+ ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference();
+ ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference();
+
// Up(x) + Prior(x)
- fixed (byte* scan = scanline)
- fixed (byte* prev = previousScanline)
+ for (int x = 1; x < scanline.Length; 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 scanBaseRef, x);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ scan = (byte)((scan + above) % 256);
}
}
@@ -43,21 +43,24 @@ namespace ImageSharp.Formats
/// The previous scanline.
/// The filtered scanline result.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result)
+ public static void Encode(BufferSpan scanline, BufferSpan previousScanline, BufferSpan result)
{
- // Up(x) = Raw(x) - Prior(x)
- fixed (byte* scan = scanline)
- fixed (byte* prev = previousScanline)
- fixed (byte* res = result)
- {
- res[0] = 2;
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
+
+ ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference();
+ ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference();
+ ref byte resultBaseRef = ref result.DangerousGetPinnableReference();
- for (int x = 0; x < scanline.Length; x++)
- {
- byte above = prev[x];
+ // Up(x) = Raw(x) - Prior(x)
+ resultBaseRef = 2;
- res[x + 1] = (byte)((scan[x] - above) % 256);
- }
+ for (int x = 0; x < scanline.Length; x++)
+ {
+ 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/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 67a00efef..13b4c1167 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
@@ -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)
@@ -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)
{
@@ -437,45 +430,38 @@ namespace ImageSharp.Formats
}
this.currentRowBytesRead = 0;
- FilterType filterType = (FilterType)this.scanline[0];
+ var filterType = (FilterType)this.scanline[0];
switch (filterType)
{
case FilterType.None:
-
- NoneFilter.Decode(this.scanline);
-
break;
case FilterType.Sub:
- SubFilter.Decode(this.scanline, this.bytesPerScanline, this.bytesPerPixel);
-
+ SubFilter.Decode(this.scanline, this.bytesPerPixel);
break;
case FilterType.Up:
- UpFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline);
-
+ UpFilter.Decode(this.scanline, this.previousScanline);
break;
case FilterType.Average:
- AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline, this.bytesPerPixel);
-
+ AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel);
break;
case FilterType.Paeth:
- PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline, this.bytesPerPixel);
-
+ PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel);
break;
default:
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++;
@@ -508,7 +494,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)
{
@@ -517,45 +503,40 @@ namespace ImageSharp.Formats
this.currentRowBytesRead = 0;
- FilterType filterType = (FilterType)this.scanline[0];
+ BufferSpan scanSpan = this.scanline.Slice(0, bytesPerInterlaceScanline);
+ BufferSpan prevSpan = this.previousScanline.Span.Slice(0, bytesPerInterlaceScanline);
+ var filterType = (FilterType)scanSpan[0];
switch (filterType)
{
case FilterType.None:
-
- NoneFilter.Decode(this.scanline);
-
break;
case FilterType.Sub:
- SubFilter.Decode(this.scanline, bytesPerInterlaceScanline, this.bytesPerPixel);
-
+ SubFilter.Decode(scanSpan, this.bytesPerPixel);
break;
case FilterType.Up:
- UpFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline);
-
+ UpFilter.Decode(scanSpan, prevSpan);
break;
case FilterType.Average:
- AverageFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
-
+ AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel);
break;
case FilterType.Paeth:
- PaethFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
-
+ PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel);
break;
default:
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);
@@ -583,9 +564,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 +628,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 +685,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 +883,7 @@ namespace ImageSharp.Formats
///
private PngChunk ReadChunk()
{
- PngChunk chunk = new PngChunk();
+ var chunk = new PngChunk();
this.ReadChunkLength(chunk);
if (chunk.Length < 0)
{
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 31e8cd90e..44f1513ee 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;
@@ -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.
@@ -71,25 +71,45 @@ namespace ImageSharp.Formats
///
private int bytesPerPixel;
+ ///
+ /// 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.
@@ -177,7 +197,7 @@ namespace ImageSharp.Formats
this.bytesPerPixel = this.CalculateBytesPerPixel();
- PngHeader header = new PngHeader
+ var header = new PngHeader
{
Width = image.Width,
Height = image.Height,
@@ -207,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.
///
@@ -268,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++)
@@ -286,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];
}
}
}
@@ -302,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 (PixelArea 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);
}
}
@@ -320,81 +359,79 @@ 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()
{
+ 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.
- UpFilter.Encode(rawScanline, previousScanline, this.up);
+ UpFilter.Encode(scanSpan, prevSpan, this.up);
+
int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue);
int lowestSum = currentSum;
- result = this.up;
+ Buffer actualResult = this.up;
- PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel);
+ PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel);
currentSum = this.CalculateTotalVariation(this.paeth, currentSum);
if (currentSum < lowestSum)
{
lowestSum = currentSum;
- result = this.paeth;
+ actualResult = this.paeth;
}
- SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel);
+ SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel);
currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue);
if (currentSum < lowestSum)
{
lowestSum = currentSum;
- result = this.sub;
+ actualResult = this.sub;
}
- AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel);
+ AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel);
currentSum = this.CalculateTotalVariation(this.average, currentSum);
if (currentSum < lowestSum)
{
- result = this.average;
+ actualResult = this.average;
}
- return result;
+ return actualResult;
}
///
@@ -404,17 +441,19 @@ namespace ImageSharp.Formats
/// The scanline bytes
/// The last variation sum
/// The
- private int CalculateTotalVariation(byte[] scanline, int lastSum)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int CalculateTotalVariation(BufferSpan scanline, int lastSum)
{
+ ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference();
int sum = 0;
- for (int i = 1; i < scanline.Length; i++)
+ for (int i = 1; i < this.bytesPerScanline; i++)
{
- byte v = scanline[i];
+ byte v = Unsafe.Add(ref scanBaseRef, i);
sum += v < 128 ? v : 256 - v;
// No point continuing if we are larger.
- if (sum > lastSum)
+ if (sum >= lastSum)
{
break;
}
@@ -601,18 +640,19 @@ 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;
- byte[] result = new byte[resultLength];
+ this.bytesPerScanline = this.width * this.bytesPerPixel;
+ int resultLength = this.bytesPerScanline + 1;
+
+ 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;
@@ -621,13 +661,14 @@ 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++)
{
- 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);
}
}