Browse Source

Speed up png plus fix encoding bug

af/merge-core
James Jackson-South 9 years ago
parent
commit
b033b7d81a
  1. 74
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 40
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

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

@ -178,7 +178,7 @@ namespace ImageSharp.Formats
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk. /// Thrown if the stream does not contain and end chunk.
/// </exception> /// </exception>
/// <exception cref="System.ArgumentOutOfRangeException"> /// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size. /// Thrown if the image is larger than the maximum allowable size.
/// </exception> /// </exception>
/// <returns>The decoded image</returns> /// <returns>The decoded image</returns>
@ -189,7 +189,6 @@ namespace ImageSharp.Formats
this.currentStream = stream; this.currentStream = stream;
this.currentStream.Skip(8); this.currentStream.Skip(8);
Image<TPixel> image = null; Image<TPixel> image = null;
PixelAccessor<TPixel> pixels = null;
try try
{ {
using (var deframeStream = new ZlibInflateStream(this.currentStream)) using (var deframeStream = new ZlibInflateStream(this.currentStream))
@ -211,11 +210,11 @@ namespace ImageSharp.Formats
case PngChunkTypes.Data: case PngChunkTypes.Data:
if (image == null) if (image == null)
{ {
this.InitializeImage(metadata, out image, out pixels); this.InitializeImage(metadata, out image);
} }
deframeStream.AllocateNewBytes(currentChunk.Length); deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, pixels); this.ReadScanlines(deframeStream.CompressedStream, image);
stream.Read(this.crcBuffer, 0, 4); stream.Read(this.crcBuffer, 0, 4);
break; break;
case PngChunkTypes.Palette: case PngChunkTypes.Palette:
@ -252,7 +251,6 @@ namespace ImageSharp.Formats
} }
finally finally
{ {
pixels?.Dispose();
this.scanline?.Dispose(); this.scanline?.Dispose();
this.previousScanline?.Dispose(); this.previousScanline?.Dispose();
} }
@ -324,8 +322,7 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The type the pixels will be</typeparam> /// <typeparam name="TPixel">The type the pixels will be</typeparam>
/// <param name="metadata">The metadata information for the image</param> /// <param name="metadata">The metadata information for the image</param>
/// <param name="image">The image that we will populate</param> /// <param name="image">The image that we will populate</param>
/// <param name="pixels">The pixel accessor</param> private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image)
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image, out PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.header.Width > Image<TPixel>.MaxWidth || this.header.Height > Image<TPixel>.MaxHeight) if (this.header.Width > Image<TPixel>.MaxWidth || this.header.Height > Image<TPixel>.MaxHeight)
@ -334,7 +331,6 @@ namespace ImageSharp.Formats
} }
image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata); image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);
pixels = image.Lock();
this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 1; this.bytesPerSample = 1;
@ -398,17 +394,17 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param> /// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels"> The pixel data.</param> /// <param name="image"> The pixel data.</param>
private void ReadScanlines<TPixel>(Stream dataStream, PixelAccessor<TPixel> pixels) private void ReadScanlines<TPixel>(Stream dataStream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{ {
this.DecodeInterlacedPixelData(dataStream, pixels); this.DecodeInterlacedPixelData(dataStream, image);
} }
else else
{ {
this.DecodePixelData(dataStream, pixels); this.DecodePixelData(dataStream, image);
} }
} }
@ -417,8 +413,8 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="pixels">The image pixel accessor.</param> /// <param name="image">The image to decode to.</param>
private void DecodePixelData<TPixel>(Stream compressedStream, PixelAccessor<TPixel> pixels) private void DecodePixelData<TPixel>(Stream compressedStream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
while (this.currentRow < this.header.Height) while (this.currentRow < this.header.Height)
@ -462,7 +458,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Unknown filter type."); throw new ImageFormatException("Unknown filter type.");
} }
this.ProcessDefilteredScanline(this.scanline.Array, pixels); this.ProcessDefilteredScanline(this.scanline.Array, image);
Swap(ref this.scanline, ref this.previousScanline); Swap(ref this.scanline, ref this.previousScanline);
this.currentRow++; this.currentRow++;
@ -475,8 +471,8 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="pixels">The image pixel accessor.</param> /// <param name="image">The current image.</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, PixelAccessor<TPixel> pixels) private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
while (true) while (true)
@ -537,7 +533,8 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Unknown filter type."); throw new ImageFormatException("Unknown filter type.");
} }
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); Span<TPixel> rowSpan = image.GetRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
Swap(ref this.scanline, ref this.previousScanline); Swap(ref this.scanline, ref this.previousScanline);
@ -561,12 +558,12 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image pixels</param> /// <param name="pixels">The image</param>
private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, PixelAccessor<TPixel> pixels) private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, Image<TPixel> pixels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var color = default(TPixel); var color = default(TPixel);
Span<TPixel> pixelBuffer = pixels.GetRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow);
var scanlineBuffer = new Span<byte>(defilteredScanline, 1); var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.PngColorType) switch (this.PngColorType)
@ -578,7 +575,7 @@ namespace ImageSharp.Formats
{ {
byte intensity = (byte)(newScanline1[x] * factor); byte intensity = (byte)(newScanline1[x] * factor);
color.PackFromBytes(intensity, intensity, intensity, 255); color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[x, this.currentRow] = color; rowSpan[x] = color;
} }
break; break;
@ -593,26 +590,26 @@ namespace ImageSharp.Formats
byte alpha = defilteredScanline[offset + this.bytesPerSample]; byte alpha = defilteredScanline[offset + this.bytesPerSample];
color.PackFromBytes(intensity, intensity, intensity, alpha); color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[x, this.currentRow] = color; rowSpan[x] = color;
} }
break; break;
case PngColorType.Palette: case PngColorType.Palette:
this.ProcessScanlineFromPalette(defilteredScanline, pixels); this.ProcessScanlineFromPalette(defilteredScanline, rowSpan);
break; break;
case PngColorType.Rgb: case PngColorType.Rgb:
PixelOperations<TPixel>.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width); PixelOperations<TPixel>.Instance.PackFromXyzBytes(scanlineBuffer, rowSpan, this.header.Width);
break; break;
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
PixelOperations<TPixel>.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width); PixelOperations<TPixel>.Instance.PackFromXyzwBytes(scanlineBuffer, rowSpan, this.header.Width);
break; break;
} }
@ -623,8 +620,8 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam> /// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param> /// <param name="defilteredScanline">The scanline</param>
/// <param name="pixels">The output pixels</param> /// <param name="row">Thecurrent output image row</param>
private void ProcessScanlineFromPalette<TPixel>(byte[] defilteredScanline, PixelAccessor<TPixel> pixels) private void ProcessScanlineFromPalette<TPixel>(byte[] defilteredScanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
@ -654,7 +651,7 @@ namespace ImageSharp.Formats
color.PackFromBytes(0, 0, 0, 0); color.PackFromBytes(0, 0, 0, 0);
} }
pixels[x, this.currentRow] = color; row[x] = color;
} }
} }
else else
@ -669,7 +666,7 @@ namespace ImageSharp.Formats
byte b = palette[pixelOffset + 2]; byte b = palette[pixelOffset + 2];
color.PackFromBytes(r, g, b, 255); color.PackFromBytes(r, g, b, 255);
pixels[x, this.currentRow] = color; row[x] = color;
} }
} }
} }
@ -679,11 +676,10 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="row">The current image row.</param> /// <param name="rowSpan">The current image row.</param>
/// <param name="pixels">The image pixels</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param> /// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param> /// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TPixel>(byte[] defilteredScanline, int row, PixelAccessor<TPixel> pixels, int pixelOffset = 0, int increment = 1) private void ProcessInterlacedDefilteredScanline<TPixel>(byte[] defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var color = default(TPixel); var color = default(TPixel);
@ -697,7 +693,7 @@ namespace ImageSharp.Formats
{ {
byte intensity = (byte)(newScanline1[o] * factor); byte intensity = (byte)(newScanline1[o] * factor);
color.PackFromBytes(intensity, intensity, intensity, 255); color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[x, row] = color; rowSpan[x] = color;
} }
break; break;
@ -710,7 +706,7 @@ namespace ImageSharp.Formats
byte alpha = defilteredScanline[o + this.bytesPerSample]; byte alpha = defilteredScanline[o + this.bytesPerSample];
color.PackFromBytes(intensity, intensity, intensity, alpha); color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[x, row] = color; rowSpan[x] = color;
} }
break; break;
@ -742,7 +738,7 @@ namespace ImageSharp.Formats
color.PackFromBytes(0, 0, 0, 0); color.PackFromBytes(0, 0, 0, 0);
} }
pixels[x, row] = color; rowSpan[x] = color;
} }
} }
else else
@ -757,7 +753,7 @@ namespace ImageSharp.Formats
byte b = this.palette[offset + 2]; byte b = this.palette[offset + 2];
color.PackFromBytes(r, g, b, 255); color.PackFromBytes(r, g, b, 255);
pixels[x, row] = color; rowSpan[x] = color;
} }
} }
@ -772,7 +768,7 @@ namespace ImageSharp.Formats
byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; byte b = defilteredScanline[o + (2 * this.bytesPerSample)];
color.PackFromBytes(r, g, b, 255); color.PackFromBytes(r, g, b, 255);
pixels[x, row] = color; rowSpan[x] = color;
} }
break; break;
@ -787,7 +783,7 @@ namespace ImageSharp.Formats
byte a = defilteredScanline[o + (3 * this.bytesPerSample)]; byte a = defilteredScanline[o + (3 * this.bytesPerSample)];
color.PackFromBytes(r, g, b, a); color.PackFromBytes(r, g, b, a);
pixels[x, row] = color; rowSpan[x] = color;
} }
break; break;

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

@ -220,11 +220,7 @@ namespace ImageSharp.Formats
this.WritePhysicalChunk(stream, image); this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream); this.WriteGammaChunk(stream);
using (PixelAccessor<TPixel> pixels = image.Lock()) this.WriteDataChunks(image, stream);
{
this.WriteDataChunks(pixels, stream);
}
this.WriteEndChunk(stream); this.WriteEndChunk(stream);
stream.Flush(); stream.Flush();
} }
@ -302,9 +298,8 @@ namespace ImageSharp.Formats
/// Collects a row of grayscale pixels. /// Collects a row of grayscale pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixels accessor.</param> /// <param name="rowSpan">The image row span.</param>
/// <param name="row">The row index.</param> private void CollectGrayscaleBytes<TPixel>(Span<TPixel> rowSpan)
private void CollectGrayscaleBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
byte[] rawScanlineArray = this.rawScanline.Array; byte[] rawScanlineArray = this.rawScanline.Array;
@ -316,7 +311,7 @@ namespace ImageSharp.Formats
// Convert the color to YCbCr and store the luminance // Convert the color to YCbCr and store the luminance
// Optionally store the original color alpha. // Optionally store the original color alpha.
int offset = x * this.bytesPerPixel; int offset = x * this.bytesPerPixel;
pixels[x, row].ToXyzwBytes(this.chunkTypeBuffer, 0); rowSpan[x].ToXyzwBytes(this.chunkTypeBuffer, 0);
byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2])); byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2]));
for (int i = 0; i < this.bytesPerPixel; i++) for (int i = 0; i < this.bytesPerPixel; i++)
@ -337,13 +332,10 @@ namespace ImageSharp.Formats
/// Collects a row of true color pixel data. /// Collects a row of true color pixel data.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param> /// <param name="rowSpan">The row span.</param>
/// <param name="row">The row index.</param> private void CollecTPixelBytes<TPixel>(Span<TPixel> rowSpan)
private void CollecTPixelBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> rowSpan = pixels.GetRowSpan(row);
if (this.bytesPerPixel == 4) if (this.bytesPerPixel == 4)
{ {
PixelOperations<TPixel>.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width); PixelOperations<TPixel>.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width);
@ -359,10 +351,10 @@ namespace ImageSharp.Formats
/// Each scanline is encoded in the most optimal manner to improve compression. /// Each scanline is encoded in the most optimal manner to improve compression.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param> /// <param name="rowSpan">The row span.</param>
/// <param name="row">The row.</param> /// <param name="row">The row.</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
private Buffer<byte> EncodePixelRow<TPixel>(PixelAccessor<TPixel> pixels, int row) private Buffer<byte> EncodePixelRow<TPixel>(Span<TPixel> rowSpan, int row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
switch (this.pngColorType) switch (this.pngColorType)
@ -372,10 +364,10 @@ namespace ImageSharp.Formats
break; break;
case PngColorType.Grayscale: case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(pixels, row); this.CollectGrayscaleBytes(rowSpan);
break; break;
default: default:
this.CollecTPixelBytes(pixels, row); this.CollecTPixelBytes(rowSpan);
break; break;
} }
@ -637,17 +629,17 @@ namespace ImageSharp.Formats
/// Writes the pixel information to the stream. /// Writes the pixel information to the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor.</param> /// <param name="pixels">The image.</param>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(PixelAccessor<TPixel> pixels, Stream stream) private void WriteDataChunks<TPixel>(Image<TPixel> pixels, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
this.bytesPerScanline = this.width * this.bytesPerPixel; this.bytesPerScanline = this.width * this.bytesPerPixel;
int resultLength = this.bytesPerScanline + 1; int resultLength = this.bytesPerScanline + 1;
this.previousScanline = new Buffer<byte>(this.bytesPerScanline); this.previousScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.rawScanline = new Buffer<byte>(this.bytesPerScanline); this.rawScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.result = new Buffer<byte>(resultLength); this.result = Buffer<byte>.CreateClean(resultLength);
if (this.pngColorType != PngColorType.Palette) if (this.pngColorType != PngColorType.Palette)
{ {
@ -667,7 +659,7 @@ namespace ImageSharp.Formats
{ {
for (int y = 0; y < this.height; y++) for (int y = 0; y < this.height; y++)
{ {
Buffer<byte> r = this.EncodePixelRow(pixels, y); Buffer<byte> r = this.EncodePixelRow(pixels.GetRowSpan(y), y);
deflateStream.Write(r.Array, 0, resultLength); deflateStream.Write(r.Array, 0, resultLength);
Swap(ref this.rawScanline, ref this.previousScanline); Swap(ref this.rawScanline, ref this.previousScanline);

Loading…
Cancel
Save