Browse Source

Speed up png plus fix encoding bug

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

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

@ -220,11 +220,7 @@ namespace ImageSharp.Formats
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
using (PixelAccessor<TPixel> pixels = image.Lock())
{
this.WriteDataChunks(pixels, stream);
}
this.WriteDataChunks(image, stream);
this.WriteEndChunk(stream);
stream.Flush();
}
@ -302,9 +298,8 @@ namespace ImageSharp.Formats
/// Collects a row of grayscale pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixels accessor.</param>
/// <param name="row">The row index.</param>
private void CollectGrayscaleBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
/// <param name="rowSpan">The image row span.</param>
private void CollectGrayscaleBytes<TPixel>(Span<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
@ -316,7 +311,7 @@ namespace ImageSharp.Formats
// Convert the color to YCbCr and store the luminance
// Optionally store the original color alpha.
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]));
for (int i = 0; i < this.bytesPerPixel; i++)
@ -337,13 +332,10 @@ namespace ImageSharp.Formats
/// Collects a row of true color pixel data.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param>
/// <param name="row">The row index.</param>
private void CollecTPixelBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
/// <param name="rowSpan">The row span.</param>
private void CollecTPixelBytes<TPixel>(Span<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
Span<TPixel> rowSpan = pixels.GetRowSpan(row);
if (this.bytesPerPixel == 4)
{
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.
/// </summary>
/// <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>
/// <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>
{
switch (this.pngColorType)
@ -372,10 +364,10 @@ namespace ImageSharp.Formats
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(pixels, row);
this.CollectGrayscaleBytes(rowSpan);
break;
default:
this.CollecTPixelBytes(pixels, row);
this.CollecTPixelBytes(rowSpan);
break;
}
@ -637,17 +629,17 @@ namespace ImageSharp.Formats
/// Writes the pixel information to the stream.
/// </summary>
/// <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>
private void WriteDataChunks<TPixel>(PixelAccessor<TPixel> pixels, Stream stream)
private void WriteDataChunks<TPixel>(Image<TPixel> pixels, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.width * this.bytesPerPixel;
int resultLength = this.bytesPerScanline + 1;
this.previousScanline = new Buffer<byte>(this.bytesPerScanline);
this.rawScanline = new Buffer<byte>(this.bytesPerScanline);
this.result = new Buffer<byte>(resultLength);
this.previousScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.rawScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.result = Buffer<byte>.CreateClean(resultLength);
if (this.pngColorType != PngColorType.Palette)
{
@ -667,7 +659,7 @@ namespace ImageSharp.Formats
{
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);
Swap(ref this.rawScanline, ref this.previousScanline);

Loading…
Cancel
Save