Browse Source

Merge pull request #548 from woutware/Performance-improvements-1

Performance improvements in the PNG filtering (roughly 20% gain on my machine).
af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
209ccfdf8f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
  2. 63
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  3. 50
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  4. 55
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  5. 12
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  6. 6
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  7. 32
      src/ImageSharp/PixelFormats/Bgra32.cs
  8. 3
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

10
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs

@ -100,6 +100,8 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
using (BasicArrayBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(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<float> 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<float> data, int lo, int hi)

63
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,25 +67,24 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
resultBaseRef = 3;
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;
}
int x = 0;
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;
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 /* 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);
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - Average(left, above));
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
sum -= 3;

50
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -30,21 +30,22 @@ 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);
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,26 +71,25 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
resultBaseRef = 4;
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;
}
int x = 0;
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;
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 /* 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);
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));
}
sum -= 4;
@ -106,7 +106,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);

55
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,23 +58,22 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Sub(x) = Raw(x) - Raw(x-bpp)
resultBaseRef = 1;
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;
}
int x = 0;
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);
res = scan;
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
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;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - prev);
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
sum -= 1;

12
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,13 +57,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Up(x) = Raw(x) - Prior(x)
resultBaseRef = 2;
for (int x = 0; x < scanline.Length; x++)
{
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);
ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1);
res = (byte)((scan - above) % 256);
sum += res < 128 ? res : 256 - res;
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - above);
sum += ImageMaths.FastAbs(unchecked((sbyte)res));
}
sum -= 2;

6
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -321,9 +321,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>The <see cref="T:byte[]"/></returns>
private IManagedByteBuffer GetOptimalFilteredScanline()
{
Span<byte> scanSpan = this.rawScanline.Span;
Span<byte> 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<byte> scanSpan = this.rawScanline.Span;
Span<byte> 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);

32
src/ImageSharp/PixelFormats/Bgra32.cs

@ -38,6 +38,16 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary>
public byte A;
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
@ -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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return this.ToRgba32().ToVector4();
return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
/// <inheritdoc/>
@ -243,5 +251,21 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>The RGBA value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgra32 ToBgra32() => this;
/// <summary>
/// Packs a <see cref="Vector4"/> into a color.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
[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;
}
}
}

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