Browse Source

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.
af/merge-core
woutware 8 years ago
parent
commit
c7e6d19cc1
  1. 22
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  2. 26
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  3. 20
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  4. 11
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  5. 3
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

22
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;
}

26
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 <see cref="byte"/>.
/// </returns>
[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);

20
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;
}

11
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;
}

3
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<Rgba32>(10, 10);
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10);
}

Loading…
Cancel
Save