Browse Source

Pass filtered result scanline by reference.

Halfs the memory usage
pull/23/head
James Jackson-South 10 years ago
parent
commit
f814dc3609
  1. 3
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  2. 6
      src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
  3. 4
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  4. 4
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  5. 52
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

3
src/ImageSharp/Formats/Png/Filters/AverageFilter.cs

@ -52,10 +52,9 @@ namespace ImageSharp.Formats
/// <param name="bytesPerPixel">The bytes per pixel.</param> /// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel, int bytesPerScanline) public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
{ {
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
byte[] result = new byte[bytesPerScanline + 1];
fixed (byte* scan = scanline) fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline) fixed (byte* prev = previousScanline)
fixed (byte* res = result) fixed (byte* res = result)

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

@ -30,15 +30,11 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> public static void Encode(byte[] scanline, byte[] result, int bytesPerScanline)
public static byte[] Encode(byte[] scanline, int bytesPerScanline)
{ {
// Insert a byte before the data. // Insert a byte before the data.
byte[] result = new byte[bytesPerScanline + 1];
result[0] = 0; result[0] = 0;
Buffer.BlockCopy(scanline, 0, result, 1, bytesPerScanline); Buffer.BlockCopy(scanline, 0, result, 1, bytesPerScanline);
return result;
} }
} }
} }

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

@ -52,11 +52,9 @@ namespace ImageSharp.Formats
/// <param name="bytesPerPixel">The bytes per pixel.</param> /// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel, int bytesPerScanline) public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
{ {
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
byte[] result = new byte[bytesPerScanline + 1];
fixed (byte* scan = scanline) fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline) fixed (byte* prev = previousScanline)
fixed (byte* res = result) fixed (byte* res = result)

4
src/ImageSharp/Formats/Png/Filters/UpFilter.cs

@ -45,11 +45,9 @@ namespace ImageSharp.Formats
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, int bytesPerScanline, byte[] previousScanline) public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
{ {
// Up(x) = Raw(x) - Prior(x) // Up(x) = Raw(x) - Prior(x)
byte[] result = new byte[bytesPerScanline + 1];
fixed (byte* scan = scanline) fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline) fixed (byte* prev = previousScanline)
fixed (byte* res = result) fixed (byte* res = result)

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

@ -313,9 +313,9 @@ namespace ImageSharp.Formats
/// <param name="row">The row.</param> /// <param name="row">The row.</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <param name="rawScanline">The raw scanline.</param> /// <param name="rawScanline">The raw scanline.</param>
/// <param name="result">The resultant filtered scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline.</param> /// <param name="bytesPerScanline">The number of bytes per scanline.</param>
/// <returns>The <see cref="T:byte[]"/></returns> private void EncodePixelRow<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result, int bytesPerScanline)
private byte[] EncodePixelRow<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] previousScanline, byte[] rawScanline, int bytesPerScanline)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
@ -333,9 +333,7 @@ namespace ImageSharp.Formats
break; break;
} }
byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel); this.GetOptimalFilteredScanline(rawScanline, previousScanline, result, bytesPerScanline);
return filteredScanline;
} }
/// <summary> /// <summary>
@ -344,37 +342,30 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="rawScanline">The raw scanline</param> /// <param name="rawScanline">The raw scanline</param>
/// <param name="previousScanline">The previous scanline</param> /// <param name="previousScanline">The previous scanline</param>
/// <param name="result">The filtered scanline result</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bytesPerPixel">The number of bytes per pixel</param> private void GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{ {
Tuple<byte[], int>[] candidates;
// Palette images don't compress well with adaptive filtering. // Palette images don't compress well with adaptive filtering.
if (this.PngColorType == PngColorType.Palette) if (this.PngColorType == PngColorType.Palette)
{ {
candidates = new Tuple<byte[], int>[1]; NoneFilter.Encode(rawScanline, result, bytesPerScanline);
return;
byte[] none = NoneFilter.Encode(rawScanline, bytesPerScanline);
candidates[0] = new Tuple<byte[], int>(none, this.CalculateTotalVariation(none));
} }
else
{
candidates = new Tuple<byte[], int>[4];
byte[] sub = SubFilter.Encode(rawScanline, bytesPerPixel, bytesPerScanline); Tuple<byte[], int>[] candidates = new Tuple<byte[], int>[4];
candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub));
byte[] up = UpFilter.Encode(rawScanline, bytesPerScanline, previousScanline); byte[] sub = SubFilter.Encode(rawScanline, this.bytesPerPixel, bytesPerScanline);
candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up)); candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub));
byte[] average = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline); byte[] up = UpFilter.Encode(rawScanline, previousScanline, result, bytesPerScanline);
candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average)); candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up));
byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline); byte[] average = AverageFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth)); candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average));
}
byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth));
int lowestTotalVariation = int.MaxValue; int lowestTotalVariation = int.MaxValue;
int lowestTotalVariationIndex = 0; int lowestTotalVariationIndex = 0;
@ -388,7 +379,8 @@ namespace ImageSharp.Formats
} }
} }
return candidates[lowestTotalVariationIndex].Item1; // ReSharper disable once RedundantAssignment
result = candidates[lowestTotalVariationIndex].Item1;
} }
/// <summary> /// <summary>
@ -599,6 +591,7 @@ namespace ImageSharp.Formats
int bytesPerScanline = this.width * this.bytesPerPixel; int bytesPerScanline = this.width * this.bytesPerPixel;
byte[] previousScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline); byte[] previousScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
byte[] rawScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline); byte[] rawScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
byte[] result = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1);
byte[] buffer; byte[] buffer;
int bufferLength; int bufferLength;
@ -610,8 +603,8 @@ namespace ImageSharp.Formats
{ {
for (int y = 0; y < this.height; y++) for (int y = 0; y < this.height; y++)
{ {
byte[] data = this.EncodePixelRow(pixels, y, previousScanline, rawScanline, bytesPerScanline); this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result, bytesPerScanline);
deflateStream.Write(data, 0, data.Length); deflateStream.Write(result, 0, bytesPerScanline + 1);
deflateStream.Flush(); deflateStream.Flush();
// Do a bit of shuffling; // Do a bit of shuffling;
@ -628,6 +621,7 @@ namespace ImageSharp.Formats
{ {
ArrayPool<byte>.Shared.Return(previousScanline); ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(rawScanline); ArrayPool<byte>.Shared.Return(rawScanline);
ArrayPool<byte>.Shared.Return(result);
memoryStream?.Dispose(); memoryStream?.Dispose();
} }

Loading…
Cancel
Save