From c7e6d19cc1c3369c48b1908170188fcfc8292e6e Mon Sep 17 00:00:00 2001
From: woutware <35376607+woutware@users.noreply.github.com>
Date: Fri, 27 Apr 2018 18:54:53 +0200
Subject: [PATCH 1/4] Performance improvements.
Ditched the use of modulo (%). Modulo is slow, and if you use it to just get the lower 8 bits (1 byte), you can just directly cast to byte.
Also moved one if < out of the loop for speed. The less branching the better.
Similarly replaced an if < 128 by cast to sbyte and taking its abs value.
Performance gain in writing to PNG file seems to be roughly 20% (release mode, 1000x1000 bitmap).
In ImageFramesCollectionTests I've set the culture to en-US to ensure getting english exception texts.
---
.../Formats/Png/Filters/AverageFilter.cs | 22 ++++++++++++++++
.../Formats/Png/Filters/PaethFilter.cs | 26 +++++++++++++++++--
.../Formats/Png/Filters/SubFilter.cs | 20 ++++++++++++++
.../Formats/Png/Filters/UpFilter.cs | 11 ++++++++
.../Image/ImageFramesCollectionTests.cs | 3 +++
5 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index de62d4702..e832b2d7f 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -69,6 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
resultBaseRef = 3;
+#if OLD_AND_SLOW
for (int x = 0; x < scanline.Length; x++)
{
if (x - bytesPerPixel < 0)
@@ -89,6 +90,27 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += res < 128 ? res : 256 - res;
}
}
+#else
+ int x = 0;
+ for (; x < bytesPerPixel;) {
+ byte scan = Unsafe.Add(ref scanBaseRef, x);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
+ res = (byte)(scan - (above >> 1));
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+
+ for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft) {
+ byte scan = Unsafe.Add(ref scanBaseRef, x);
+ byte left = Unsafe.Add(ref scanBaseRef, xLeft);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
+ res = (byte)(scan - Average(left, above));
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+#endif
sum -= 3;
}
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index 7e05d736f..652269e27 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
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));
+ scan = (byte)(scan + PaethPredictor(left, above, upperLeft));
}
}
@@ -70,6 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
resultBaseRef = 4;
+#if OLD_AND_SLOW
for (int x = 0; x < scanline.Length; x++)
{
if (x - bytesPerPixel < 0)
@@ -91,6 +92,27 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += res < 128 ? res : 256 - res;
}
}
+#else
+ int x = 0;
+ for (; x < bytesPerPixel; ++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 - PaethPredictor(0, above, 0));
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+
+ for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft) {
+ byte scan = Unsafe.Add(ref scanBaseRef, x);
+ byte left = Unsafe.Add(ref scanBaseRef, xLeft);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ byte upperLeft = Unsafe.Add(ref prevBaseRef, xLeft);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
+ res = (byte)(scan - PaethPredictor(left, above, upperLeft));
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+#endif
sum -= 4;
}
@@ -106,7 +128,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
/// The .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static byte PaethPredicator(byte left, byte above, byte upperLeft)
+ private static byte PaethPredictor(byte left, byte above, byte upperLeft)
{
int p = left + above - upperLeft;
int pa = ImageMaths.FastAbs(p - left);
diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
index c0db7da93..f389cb2cd 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -60,6 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Sub(x) = Raw(x) - Raw(x-bpp)
resultBaseRef = 1;
+#if OLD_AND_SLOW
for (int x = 0; x < scanline.Length; x++)
{
if (x - bytesPerPixel < 0)
@@ -78,6 +79,25 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += res < 128 ? res : 256 - res;
}
}
+#else
+ int x = 0;
+ for (; x < bytesPerPixel;) {
+ byte scan = Unsafe.Add(ref scanBaseRef, x);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
+ res = scan;
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+
+ for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft) {
+ byte scan = Unsafe.Add(ref scanBaseRef, x);
+ byte prev = Unsafe.Add(ref scanBaseRef, xLeft);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
+ res = (byte)(scan - prev);
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+#endif
sum -= 1;
}
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index 81c063ea9..45ece23ef 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -57,6 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Up(x) = Raw(x) - Prior(x)
resultBaseRef = 2;
+#if OLD_AND_SLOW
for (int x = 0; x < scanline.Length; x++)
{
byte scan = Unsafe.Add(ref scanBaseRef, x);
@@ -65,6 +66,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
res = (byte)((scan - above) % 256);
sum += res < 128 ? res : 256 - res;
}
+#else
+ for (int x = 0; x < scanline.Length;) {
+ byte scan = Unsafe.Add(ref scanBaseRef, x);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
+ res = (byte)(scan - above);
+ sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ }
+#endif
sum -= 2;
}
diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
index 4f00931de..4c760e681 100644
--- a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
@@ -15,6 +15,9 @@ namespace SixLabors.ImageSharp.Tests
public ImageFramesCollectionTests()
{
+ // Needed to get English exception messages, which are checked in several tests.
+ System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
+
this.image = new Image(10, 10);
this.collection = new ImageFrameCollection(this.image, 10, 10);
}
From 7278f5b2239289bfbfca886d69af9873c355be76 Mon Sep 17 00:00:00 2001
From: woutware <35376607+woutware@users.noreply.github.com>
Date: Fri, 27 Apr 2018 20:20:20 +0200
Subject: [PATCH 2/4] Removed some overhead in the Bgra32.ToVector4 and
PackFromVector4 methods.
It's now also more inline with the Rgba32 implementation.
---
src/ImageSharp/PixelFormats/Bgra32.cs | 32 +++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/Bgra32.cs
index 20dfda504..de660c05b 100644
--- a/src/ImageSharp/PixelFormats/Bgra32.cs
+++ b/src/ImageSharp/PixelFormats/Bgra32.cs
@@ -38,6 +38,16 @@ namespace SixLabors.ImageSharp.PixelFormats
///
public byte A;
+ ///
+ /// The maximum byte value.
+ ///
+ private static readonly Vector4 MaxBytes = new Vector4(255);
+
+ ///
+ /// The half vector value.
+ ///
+ private static readonly Vector4 Half = new Vector4(0.5F);
+
///
/// Initializes a new instance of the struct.
///
@@ -141,16 +151,14 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
- var rgba = default(Rgba32);
- rgba.PackFromVector4(vector);
- this.PackFromRgba32(rgba);
+ this.Pack(ref vector);
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
- return this.ToRgba32().ToVector4();
+ return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
///
@@ -243,5 +251,21 @@ namespace SixLabors.ImageSharp.PixelFormats
/// The RGBA value
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgra32 ToBgra32() => this;
+
+ ///
+ /// Packs a into a color.
+ ///
+ /// The vector containing the values to pack.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void Pack(ref Vector4 vector) {
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ this.R = (byte)vector.X;
+ this.G = (byte)vector.Y;
+ this.B = (byte)vector.Z;
+ this.A = (byte)vector.W;
+ }
}
}
\ No newline at end of file
From 8fc424a38af9b0cea84cb81c8dbc4930033322a8 Mon Sep 17 00:00:00 2001
From: woutware <35376607+woutware@users.noreply.github.com>
Date: Sat, 28 Apr 2018 11:19:59 +0200
Subject: [PATCH 3/4] Minor performance refactorings.
Moved the division out of the loop, division is expensive.
---
.../Drawing/Processors/FillRegionProcessor.cs | 10 +++++-----
src/ImageSharp/Formats/Png/PngEncoderCore.cs | 6 +++---
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
index aa0b2e9b2..95ac3fe29 100644
--- a/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
@@ -100,6 +100,8 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
using (BasicArrayBuffer scanline = source.MemoryManager.AllocateFake(scanlineWidth))
{
bool scanlineDirty = true;
+ float subpixelFraction = 1f / subpixelCount;
+ float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (int y = minY; y < maxY; y++)
{
if (scanlineDirty)
@@ -113,9 +115,8 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
scanlineDirty = false;
}
- float subpixelFraction = 1f / subpixelCount;
- float subpixelFractionPoint = subpixelFraction / subpixelCount;
- for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
+ float yPlusOne = y + 1;
+ for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer.Array, 0);
if (pointsFound == 0)
@@ -197,8 +198,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
private static void QuickSort(Span data)
{
- int hi = Math.Min(data.Length - 1, data.Length - 1);
- QuickSort(data, 0, hi);
+ QuickSort(data, 0, data.Length - 1);
}
private static void QuickSort(Span data, int lo, int hi)
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 777ee1f54..b95e102c7 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -321,9 +321,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The
private IManagedByteBuffer GetOptimalFilteredScanline()
{
- Span scanSpan = this.rawScanline.Span;
- Span prevSpan = this.previousScanline.Span;
-
// Palette images don't compress well with adaptive filtering.
if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
{
@@ -331,6 +328,9 @@ namespace SixLabors.ImageSharp.Formats.Png
return this.result;
}
+ Span scanSpan = this.rawScanline.Span;
+ Span prevSpan = this.previousScanline.Span;
+
// 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(scanSpan, prevSpan, this.up.Span, out int currentSum);
From 88fd0674b9c78195738835fafc21729a25f92c89 Mon Sep 17 00:00:00 2001
From: woutware <35376607+woutware@users.noreply.github.com>
Date: Sat, 28 Apr 2018 11:57:34 +0200
Subject: [PATCH 4/4] Processed review comments and did similar refactoring to
the PNG decoding.
---
.../Formats/Png/Filters/AverageFilter.cs | 53 +++++--------------
.../Formats/Png/Filters/PaethFilter.cs | 38 +++----------
.../Formats/Png/Filters/SubFilter.cs | 47 +++++-----------
.../Formats/Png/Filters/UpFilter.cs | 15 +-----
4 files changed, 36 insertions(+), 117 deletions(-)
diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index e832b2d7f..ffcf9b0f3 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -29,21 +29,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
- for (int x = 1; x < scanline.Length; x++)
+ int x = 1;
+ for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x) {
+ ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
+ byte above = Unsafe.Add(ref prevBaseRef, x);
+ scan = (byte)(scan + (above >> 1));
+ }
+
+ for (; x < scanline.Length; ++x)
{
- if (x - bytesPerPixel < 1)
- {
- 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);
- }
+ 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));
}
}
@@ -69,30 +67,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
resultBaseRef = 3;
-#if OLD_AND_SLOW
- for (int x = 0; x < scanline.Length; x++)
- {
- if (x - bytesPerPixel < 0)
- {
- 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);
- sum += res < 128 ? res : 256 - res;
- }
- 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);
- sum += res < 128 ? res : 256 - res;
- }
- }
-#else
int x = 0;
- for (; x < bytesPerPixel;) {
+ for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
++x;
@@ -101,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
- for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft) {
+ for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte left = Unsafe.Add(ref scanBaseRef, xLeft);
byte above = Unsafe.Add(ref prevBaseRef, x);
@@ -110,7 +86,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
res = (byte)(scan - Average(left, above));
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
-#endif
sum -= 3;
}
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index 652269e27..0d3df079c 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -30,15 +30,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
- int offset = bytesPerPixel + 1;
- for (int x = 1; x < offset; x++)
+ int offset = bytesPerPixel + 1; // Add one bcause x starts at one.
+ int x = 1;
+ for (; x < offset; x++)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
scan = (byte)(scan + above);
}
- for (int x = offset; x < scanline.Length; x++)
+ for (; x < scanline.Length; x++)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel);
@@ -70,39 +71,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
resultBaseRef = 4;
-#if OLD_AND_SLOW
- for (int x = 0; x < scanline.Length; x++)
- {
- if (x - bytesPerPixel < 0)
- {
- 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);
- sum += res < 128 ? res : 256 - res;
- }
- 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);
- sum += res < 128 ? res : 256 - res;
- }
- }
-#else
int x = 0;
- for (; x < bytesPerPixel; ++x) {
+ for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
- ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1);
+ ++x;
+ ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - PaethPredictor(0, above, 0));
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
- for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft) {
+ for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte left = Unsafe.Add(ref scanBaseRef, xLeft);
byte above = Unsafe.Add(ref prevBaseRef, x);
@@ -112,7 +91,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
res = (byte)(scan - PaethPredictor(left, above, upperLeft));
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
-#endif
sum -= 4;
}
diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
index f389cb2cd..cfb7781be 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -25,19 +25,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
// Sub(x) + Raw(x-bpp)
- for (int x = 1; x < scanline.Length; x++)
+ int x = 1;
+ for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x)
{
- if (x - bytesPerPixel < 1)
- {
- ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
- scan = (byte)(scan % 256);
- }
- else
- {
- ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
- byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel);
- scan = (byte)((scan + prev) % 256);
- }
+ ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
+ }
+
+ for (; x < scanline.Length; ++x)
+ {
+ ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
+ byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel);
+ scan = (byte)(scan + prev);
}
}
@@ -60,28 +58,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Sub(x) = Raw(x) - Raw(x-bpp)
resultBaseRef = 1;
-#if OLD_AND_SLOW
- for (int x = 0; x < scanline.Length; x++)
- {
- if (x - bytesPerPixel < 0)
- {
- byte scan = Unsafe.Add(ref scanBaseRef, x);
- ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1);
- res = (byte)(scan % 256);
- sum += res < 128 ? res : 256 - res;
- }
- 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);
- sum += res < 128 ? res : 256 - res;
- }
- }
-#else
int x = 0;
- for (; x < bytesPerPixel;) {
+ for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
@@ -89,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
- for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft) {
+ for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte prev = Unsafe.Add(ref scanBaseRef, xLeft);
++x;
@@ -97,7 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
res = (byte)(scan - prev);
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
-#endif
sum -= 1;
}
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index 45ece23ef..c6a297e33 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
- scan = (byte)((scan + above) % 256);
+ scan = (byte)(scan + above);
}
}
@@ -57,17 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Up(x) = Raw(x) - Prior(x)
resultBaseRef = 2;
-#if OLD_AND_SLOW
- 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);
- sum += res < 128 ? res : 256 - res;
- }
-#else
- for (int x = 0; x < scanline.Length;) {
+ for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
++x;
@@ -75,7 +65,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
res = (byte)(scan - above);
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
-#endif
sum -= 2;
}