diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 4c2b56e515..aabf24b2fc 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 79de59cb95..a8e397a300 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 90d0fa6925..d5ec2e7dc3 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 fa67330602..30cdf65a38 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 f4e1d8261a..b165d0935c 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 3420673073..2d63539c2b 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; }