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++)
{