Browse Source

Png improvements

Test image encodes 9kb smaller and 3ms faster.
pull/85/head
James Jackson-South 9 years ago
parent
commit
d59b78e314
  1. 6
      src/ImageSharp.Formats.Png/Filters/PaethFilter.cs
  2. 36
      src/ImageSharp.Formats.Png/PngEncoderCore.cs
  3. 3
      src/ImageSharp/Common/Extensions/ByteExtensions.cs
  4. 15
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  5. 33
      tests/ImageSharp.Benchmarks/General/Abs.cs

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

@ -79,9 +79,9 @@ namespace ImageSharp.Formats
private static byte PaethPredicator(byte left, byte above, byte upperLeft) private static byte PaethPredicator(byte left, byte above, byte upperLeft)
{ {
int p = left + above - upperLeft; int p = left + above - upperLeft;
int pa = Math.Abs(p - left); int pa = ImageMaths.FastAbs(p - left);
int pb = Math.Abs(p - above); int pb = ImageMaths.FastAbs(p - above);
int pc = Math.Abs(p - upperLeft); int pc = ImageMaths.FastAbs(p - upperLeft);
if (pa <= pb && pa <= pc) if (pa <= pb && pa <= pc)
{ {

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

@ -369,33 +369,33 @@ namespace ImageSharp.Formats
} }
SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel); SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel);
int currentTotalVariation = this.CalculateTotalVariation(this.sub); int currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue);
int lowestTotalVariation = currentTotalVariation; int lowestSum = currentSum;
result = this.sub; result = this.sub;
UpFilter.Encode(rawScanline, previousScanline, this.up); UpFilter.Encode(rawScanline, previousScanline, this.up);
currentTotalVariation = this.CalculateTotalVariation(this.up); currentSum = this.CalculateTotalVariation(this.up, currentSum);
if (currentTotalVariation < lowestTotalVariation) if (currentSum < lowestSum)
{ {
lowestTotalVariation = currentTotalVariation; lowestSum = currentSum;
result = this.up; result = this.up;
} }
AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel); AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel);
currentTotalVariation = this.CalculateTotalVariation(this.average); currentSum = this.CalculateTotalVariation(this.average, currentSum);
if (currentTotalVariation < lowestTotalVariation) if (currentSum < lowestSum)
{ {
lowestTotalVariation = currentTotalVariation; lowestSum = currentSum;
result = this.average; result = this.average;
} }
PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel); PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel);
currentTotalVariation = this.CalculateTotalVariation(this.paeth); currentSum = this.CalculateTotalVariation(this.paeth, currentSum);
if (currentTotalVariation < lowestTotalVariation) if (currentSum < lowestSum)
{ {
result = this.paeth; result = this.paeth;
} }
@ -408,17 +408,25 @@ namespace ImageSharp.Formats
/// neighbor differences. /// neighbor differences.
/// </summary> /// </summary>
/// <param name="scanline">The scanline bytes</param> /// <param name="scanline">The scanline bytes</param>
/// <param name="lastSum">The last variation sum</param>
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
private int CalculateTotalVariation(byte[] scanline) private int CalculateTotalVariation(byte[] scanline, int lastSum)
{ {
int totalVariation = 0; int sum = 0;
for (int i = 1; i < scanline.Length; i++) for (int i = 1; i < scanline.Length; i++)
{ {
totalVariation += Math.Abs(scanline[i] - scanline[i - 1]); byte v = scanline[i];
sum += v < 128 ? v : 256 - v;
// No point continuing if we are larger.
if (sum > lastSum)
{
break;
}
} }
return totalVariation; return sum;
} }
/// <summary> /// <summary>

3
src/ImageSharp/Common/Extensions/ByteExtensions.cs

@ -5,6 +5,8 @@
namespace ImageSharp namespace ImageSharp
{ {
using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Extension methods for the <see cref="byte"/> struct. /// Extension methods for the <see cref="byte"/> struct.
/// </summary> /// </summary>
@ -26,6 +28,7 @@ namespace ImageSharp
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
/// <param name="length">The length.</param> /// <param name="length">The length.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception> /// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReverseBytes(this byte[] source, int index, int length) public static void ReverseBytes(this byte[] source, int index, int length)
{ {
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));

15
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -8,12 +8,26 @@ namespace ImageSharp
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Provides common mathematical methods. /// Provides common mathematical methods.
/// </summary> /// </summary>
internal static class ImageMaths internal static class ImageMaths
{ {
/// <summary>
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
/// </summary>
/// <param name="x">
/// A number that is greater than <see cref="int.MinValue"/>, but less than or equal to <see cref="int.MaxValue"/>
/// </param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int FastAbs(int x)
{
return (x ^ (x >> 31)) - (x >> 31);
}
/// <summary> /// <summary>
/// Returns how many bits are required to store the specified number of colors. /// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value. /// Performs a Log2() on the value.
@ -273,6 +287,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="float"/> /// The <see cref="float"/>
/// </returns>. /// </returns>.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Clean(float x) private static float Clean(float x)
{ {
if (Math.Abs(x) < Constants.Epsilon) if (Math.Abs(x) < Constants.Epsilon)

33
tests/ImageSharp.Benchmarks/General/Abs.cs

@ -0,0 +1,33 @@
namespace ImageSharp.Benchmarks.General
{
using System;
using BenchmarkDotNet.Attributes;
public class Abs
{
[Params(-1, 1)]
public int X { get; set; }
[Benchmark(Baseline = true, Description = "Maths Abs")]
public int MathAbs()
{
int x = this.X;
return Math.Abs(x);
}
[Benchmark(Description = "Conditional Abs")]
public int ConditionalAbs()
{
int x = this.X;
return x < 0 ? -x : x;
}
[Benchmark(Description = "Bitwise Abs")]
public int AbsBitwise()
{
int x = this.X;
return (x ^ (x >> 31)) - (x >> 31);
}
}
}
Loading…
Cancel
Save