Browse Source

Reduce allocations per filtered row

pull/23/head
James Jackson-South 10 years ago
parent
commit
a0dd79cfed
  1. 7
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  2. 7
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  3. 7
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  4. 7
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  5. 42
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

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

@ -45,15 +45,16 @@ namespace ImageSharp.Formats
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <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>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel) public static byte[] Encode(byte[] scanline, byte[] previousScanline, 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[] encodedScanline = new byte[scanline.Length + 1]; byte[] encodedScanline = new byte[bytesPerScanline + 1];
encodedScanline[0] = (byte)FilterType.Average; encodedScanline[0] = (byte)FilterType.Average;
for (int x = 0; x < scanline.Length; x++) for (int x = 0; x < bytesPerScanline; x++)
{ {
byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel];
byte above = previousScanline[x]; byte above = previousScanline[x];

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

@ -45,14 +45,15 @@ namespace ImageSharp.Formats
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <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>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel) public static byte[] Encode(byte[] scanline, byte[] previousScanline, 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[] encodedScanline = new byte[scanline.Length + 1]; byte[] encodedScanline = new byte[bytesPerScanline + 1];
encodedScanline[0] = (byte)FilterType.Paeth; encodedScanline[0] = (byte)FilterType.Paeth;
for (int x = 0; x < scanline.Length; x++) for (int x = 0; x < bytesPerScanline; x++)
{ {
byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel];
byte above = previousScanline[x]; byte above = previousScanline[x];

7
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -38,14 +38,15 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <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>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, int bytesPerPixel) public static byte[] Encode(byte[] scanline, int bytesPerPixel, int bytesPerScanline)
{ {
// Sub(x) = Raw(x) - Raw(x-bpp) // Sub(x) = Raw(x) - Raw(x-bpp)
byte[] encodedScanline = new byte[scanline.Length + 1]; byte[] encodedScanline = new byte[bytesPerScanline + 1];
encodedScanline[0] = (byte)FilterType.Sub; encodedScanline[0] = (byte)FilterType.Sub;
for (int x = 0; x < scanline.Length; x++) for (int x = 0; x < bytesPerScanline; x++)
{ {
byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel];

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

@ -37,15 +37,16 @@ namespace ImageSharp.Formats
/// Encodes the scanline /// Encodes the scanline
/// </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="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, byte[] previousScanline) public static byte[] Encode(byte[] scanline, int bytesPerScanline, byte[] previousScanline)
{ {
// Up(x) = Raw(x) - Prior(x) // Up(x) = Raw(x) - Prior(x)
byte[] encodedScanline = new byte[scanline.Length + 1]; byte[] encodedScanline = new byte[bytesPerScanline + 1];
encodedScanline[0] = (byte)FilterType.Up; encodedScanline[0] = (byte)FilterType.Up;
for (int x = 0; x < scanline.Length; x++) for (int x = 0; x < bytesPerScanline; x++)
{ {
byte above = previousScanline[x]; byte above = previousScanline[x];

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

@ -331,18 +331,24 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
private byte[] EncodePixelData() private byte[] EncodePixelData()
{ {
// TODO: Use pointers
byte[][] filteredScanlines = new byte[this.height][]; byte[][] filteredScanlines = new byte[this.height][];
byte[] previousScanline = new byte[this.width * this.bytesPerPixel]; int bytesPerScanline = this.width * this.bytesPerPixel;
int length = 0; int length = 0;
byte[] previousScanline = new byte[bytesPerScanline];
byte[] rawScanline = new byte[bytesPerScanline];
for (int y = 0; y < this.height; y++) for (int y = 0; y < this.height; y++)
{ {
byte[] rawScanline = this.GetRawScanline(y); Buffer.BlockCopy(this.pixelData, y * bytesPerScanline, rawScanline, 0, bytesPerScanline);
byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, this.bytesPerPixel); byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel);
length += filteredScanline.Length; length += filteredScanline.Length;
filteredScanlines[y] = filteredScanline; filteredScanlines[y] = filteredScanline;
previousScanline = rawScanline; // Do a bit of shuffling;
byte[] tmp = rawScanline;
rawScanline = previousScanline;
previousScanline = tmp;
} }
// Flatten the jagged array // Flatten the jagged array
@ -362,22 +368,23 @@ 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="byteCount">The number of bytes per pixel</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bytesPerPixel">The number of bytes per pixel</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int byteCount) private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{ {
Tuple<byte[], int>[] candidates = new Tuple<byte[], int>[4]; Tuple<byte[], int>[] candidates = new Tuple<byte[], int>[4];
byte[] sub = SubFilter.Encode(rawScanline, byteCount); byte[] sub = SubFilter.Encode(rawScanline, bytesPerPixel, bytesPerScanline);
candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub)); candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub));
byte[] up = UpFilter.Encode(rawScanline, previousScanline); byte[] up = UpFilter.Encode(rawScanline, bytesPerScanline, previousScanline);
candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up)); candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up));
byte[] average = AverageFilter.Encode(rawScanline, previousScanline, byteCount); byte[] average = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline);
candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average)); candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average));
byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, byteCount); byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline);
candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth)); candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth));
int lowestTotalVariation = int.MaxValue; int lowestTotalVariation = int.MaxValue;
@ -413,19 +420,6 @@ namespace ImageSharp.Formats
return totalVariation; return totalVariation;
} }
/// <summary>
/// Get the raw scanline data from the pixel data
/// </summary>
/// <param name="y">The row number</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] GetRawScanline(int y)
{
int stride = this.bytesPerPixel * this.width;
byte[] rawScanline = new byte[stride];
Buffer.BlockCopy(this.pixelData, y * stride, rawScanline, 0, stride);
return rawScanline;
}
/// <summary> /// <summary>
/// Calculates the correct number of bytes per pixel for the given color type. /// Calculates the correct number of bytes per pixel for the given color type.
/// </summary> /// </summary>

Loading…
Cancel
Save