Browse Source

Minor png codec perf improvements

af/merge-core
James Jackson-South 10 years ago
parent
commit
0bdb8dce2a
  1. 13
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 66
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

13
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -8,7 +8,6 @@ namespace ImageSharp.Formats
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Text; using System.Text;
/// <summary> /// <summary>
@ -336,7 +335,7 @@ namespace ImageSharp.Formats
byte intensity = defilteredScanline[offset]; byte intensity = defilteredScanline[offset];
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromVector4(new Vector4(intensity, intensity, intensity, 255) / 255F); color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[(row * this.header.Width) + x] = color; pixels[(row * this.header.Width) + x] = color;
} }
@ -352,7 +351,7 @@ namespace ImageSharp.Formats
byte alpha = defilteredScanline[offset + this.bytesPerSample]; byte alpha = defilteredScanline[offset + this.bytesPerSample];
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromVector4(new Vector4(intensity, intensity, intensity, alpha) / 255F); color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[(row * this.header.Width) + x] = color; pixels[(row * this.header.Width) + x] = color;
} }
@ -379,7 +378,7 @@ namespace ImageSharp.Formats
byte r = this.palette[pixelOffset]; byte r = this.palette[pixelOffset];
byte g = this.palette[pixelOffset + 1]; byte g = this.palette[pixelOffset + 1];
byte b = this.palette[pixelOffset + 2]; byte b = this.palette[pixelOffset + 2];
color.PackFromVector4(new Vector4(r, g, b, a) / 255F); color.PackFromBytes(r, g, b, a);
} }
pixels[offset] = color; pixels[offset] = color;
@ -398,7 +397,7 @@ namespace ImageSharp.Formats
byte b = this.palette[pixelOffset + 2]; byte b = this.palette[pixelOffset + 2];
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromVector4(new Vector4(r, g, b, 255) / 255F); color.PackFromBytes(r, g, b, 255);
pixels[offset] = color; pixels[offset] = color;
} }
} }
@ -416,7 +415,7 @@ namespace ImageSharp.Formats
byte b = defilteredScanline[offset + (2 * this.bytesPerSample)]; byte b = defilteredScanline[offset + (2 * this.bytesPerSample)];
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromVector4(new Vector4(r, g, b, 255) / 255F); color.PackFromBytes(r, g, b, 255);
pixels[(row * this.header.Width) + x] = color; pixels[(row * this.header.Width) + x] = color;
} }
@ -434,7 +433,7 @@ namespace ImageSharp.Formats
byte a = defilteredScanline[offset + (3 * this.bytesPerSample)]; byte a = defilteredScanline[offset + (3 * this.bytesPerSample)];
TColor color = default(TColor); TColor color = default(TColor);
color.PackFromVector4(new Vector4(r, g, b, a) / 255F); color.PackFromBytes(r, g, b, a);
pixels[(row * this.header.Width) + x] = color; pixels[(row * this.header.Width) + x] = color;
} }

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

@ -14,7 +14,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Performs the png encoding operation. /// Performs the png encoding operation.
/// TODO: Perf. There's lots of array parsing going on here. This should be unmanaged. /// TODO: Perf. There's lots of array parsing and copying going on here. This should be unmanaged.
/// </summary> /// </summary>
internal sealed class PngEncoderCore internal sealed class PngEncoderCore
{ {
@ -254,34 +254,31 @@ namespace ImageSharp.Formats
// Copy the pixels across from the image. // Copy the pixels across from the image.
this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; this.pixelData = new byte[this.width * this.height * this.bytesPerPixel];
int stride = this.width * this.bytesPerPixel; int stride = this.width * this.bytesPerPixel;
byte[] bytes = new byte[4];
using (PixelAccessor<TColor, TPacked> pixels = image.Lock()) using (PixelAccessor<TColor, TPacked> pixels = image.Lock())
{ {
Parallel.For( for (int y = 0; y < this.height; y++)
0, {
this.height, for (int x = 0; x < this.width; x++)
Bootstrapper.Instance.ParallelOptions, {
y => // Convert the color to YCbCr and store the luminance
{ // Optionally store the original color alpha.
for (int x = 0; x < this.width; x++) int dataOffset = (y * stride) + (x * this.bytesPerPixel);
{ pixels[x, y].ToBytes(bytes, 0, ComponentOrder.XYZW);
// Convert the color to YCbCr and store the luminance YCbCr luminance = new Color(bytes[0], bytes[1], bytes[2], bytes[3]);
// Optionally store the original color alpha. for (int i = 0; i < this.bytesPerPixel; i++)
int dataOffset = (y * stride) + (x * this.bytesPerPixel); {
Color source = new Color(pixels[x, y].ToVector4()); if (i == 0)
YCbCr luminance = source; {
for (int i = 0; i < this.bytesPerPixel; i++) this.pixelData[dataOffset] = (byte)luminance.Y;
{ }
if (i == 0) else
{ {
this.pixelData[dataOffset] = ((byte)luminance.Y).Clamp(0, 255); this.pixelData[dataOffset + i] = bytes[3];
} }
else }
{ }
this.pixelData[dataOffset + i] = source.A; }
}
}
}
});
} }
} }
@ -296,10 +293,12 @@ namespace ImageSharp.Formats
where TPacked : struct where TPacked : struct
{ {
// Copy the pixels across from the image. // Copy the pixels across from the image.
// TODO: This could be sped up more if we add a method to PixelAccessor that does this by row directly to a byte array.
this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; this.pixelData = new byte[this.width * this.height * this.bytesPerPixel];
int stride = this.width * this.bytesPerPixel; int stride = this.width * this.bytesPerPixel;
using (PixelAccessor<TColor, TPacked> pixels = image.Lock()) using (PixelAccessor<TColor, TPacked> pixels = image.Lock())
{ {
int bpp = this.bytesPerPixel;
Parallel.For( Parallel.For(
0, 0,
this.height, this.height,
@ -309,15 +308,7 @@ namespace ImageSharp.Formats
for (int x = 0; x < this.width; x++) for (int x = 0; x < this.width; x++)
{ {
int dataOffset = (y * stride) + (x * this.bytesPerPixel); int dataOffset = (y * stride) + (x * this.bytesPerPixel);
Color source = new Color(pixels[x, y].ToVector4()); pixels[x, y].ToBytes(this.pixelData, dataOffset, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ);
this.pixelData[dataOffset] = source.R;
this.pixelData[dataOffset + 1] = source.G;
this.pixelData[dataOffset + 2] = source.B;
if (this.bytesPerPixel == 4)
{
this.pixelData[dataOffset + 3] = source.A;
}
} }
}); });
} }
@ -330,6 +321,7 @@ 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
List<byte[]> filteredScanlines = new List<byte[]>(); List<byte[]> filteredScanlines = new List<byte[]>();
byte[] previousScanline = new byte[this.width * this.bytesPerPixel]; byte[] previousScanline = new byte[this.width * this.bytesPerPixel];
@ -344,6 +336,7 @@ namespace ImageSharp.Formats
previousScanline = rawScanline; previousScanline = rawScanline;
} }
// TODO: We should be able to use a byte array when not using interlaced encoding.
List<byte> result = new List<byte>(); List<byte> result = new List<byte>();
foreach (byte[] encodedScanline in filteredScanlines) foreach (byte[] encodedScanline in filteredScanlines)
@ -516,6 +509,7 @@ namespace ImageSharp.Formats
int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3;
byte[] colorTable = new byte[colorTableLength]; byte[] colorTable = new byte[colorTableLength];
// TODO: Optimize this.
Parallel.For( Parallel.For(
0, 0,
pixelCount, pixelCount,

Loading…
Cancel
Save