Browse Source

replaced Png byte[] -s with Buffer<byte>

af/merge-core
Anton Firszov 9 years ago
parent
commit
cd5de8a694
  1. 5
      src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
  2. 35
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 6
      src/ImageSharp/Formats/Png/PngEncoder.cs
  4. 128
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

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

@ -21,11 +21,12 @@ namespace ImageSharp.Formats
/// <param name="scanline">The scanline to encode</param>
/// <param name="result">The filtered scanline result.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(byte[] scanline, byte[] result)
public static void Encode(BufferSpan<byte> scanline, BufferSpan<byte> result)
{
// Insert a byte before the data.
result[0] = 0;
Buffer.BlockCopy(scanline, 0, result, 1, scanline.Length);
result = result.Slice(1);
BufferSpan.Copy(scanline, result);
}
}
}

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

@ -131,12 +131,12 @@ namespace ImageSharp.Formats
/// <summary>
/// Previous scanline processed
/// </summary>
private byte[] previousScanline;
private Buffer<byte> previousScanline;
/// <summary>
/// The current scanline that is being processed
/// </summary>
private byte[] scanline;
private Buffer<byte> scanline;
/// <summary>
/// The index of the current scanline being processed
@ -252,11 +252,8 @@ namespace ImageSharp.Formats
finally
{
pixels?.Dispose();
if (this.previousScanline != null)
{
ArrayPool<byte>.Shared.Return(this.previousScanline);
ArrayPool<byte>.Shared.Return(this.scanline);
}
this.scanline?.Dispose();
this.previousScanline?.Dispose();
}
}
@ -345,12 +342,8 @@ namespace ImageSharp.Formats
this.bytesPerSample = this.header.BitDepth / 8;
}
this.previousScanline = ArrayPool<byte>.Shared.Rent(this.bytesPerScanline);
this.scanline = ArrayPool<byte>.Shared.Rent(this.bytesPerScanline);
// Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero.
Array.Clear(this.scanline, 0, this.bytesPerScanline);
Array.Clear(this.previousScanline, 0, this.bytesPerScanline);
this.previousScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.scanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
}
/// <summary>
@ -429,7 +422,7 @@ namespace ImageSharp.Formats
{
while (this.currentRow < this.header.Height)
{
int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead);
int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead);
this.currentRowBytesRead += bytesRead;
if (this.currentRowBytesRead < this.bytesPerScanline)
{
@ -438,8 +431,8 @@ namespace ImageSharp.Formats
this.currentRowBytesRead = 0;
var scanSpan = new BufferSpan<byte>(this.scanline);
var prevSpan = new BufferSpan<byte>(this.previousScanline);
BufferSpan<byte> scanSpan = this.scanline.Span;
BufferSpan<byte> prevSpan = this.previousScanline.Span;
var filterType = (FilterType)scanSpan[0];
switch (filterType)
@ -471,7 +464,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Unknown filter type.");
}
this.ProcessDefilteredScanline(this.scanline, pixels);
this.ProcessDefilteredScanline(this.scanline.Array, pixels);
Swap(ref this.scanline, ref this.previousScanline);
this.currentRow++;
@ -504,7 +497,7 @@ namespace ImageSharp.Formats
while (this.currentRow < this.header.Height)
{
int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);
int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);
this.currentRowBytesRead += bytesRead;
if (this.currentRowBytesRead < bytesPerInterlaceScanline)
{
@ -513,8 +506,8 @@ namespace ImageSharp.Formats
this.currentRowBytesRead = 0;
var scanSpan = new BufferSpan<byte>(this.scanline);
var prevSpan = new BufferSpan<byte>(this.previousScanline);
BufferSpan<byte> scanSpan = this.scanline.Span;
BufferSpan<byte> prevSpan = this.previousScanline.Span;
var filterType = (FilterType)scanSpan[0];
switch (filterType)
@ -546,7 +539,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Unknown filter type.");
}
this.ProcessInterlacedDefilteredScanline(this.scanline, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
Swap(ref this.scanline, ref this.previousScanline);

6
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -33,8 +33,10 @@ namespace ImageSharp.Formats
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IPngEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
PngEncoderCore encode = new PngEncoderCore(options);
encode.Encode(image, stream);
using (var encode = new PngEncoderCore(options))
{
encode.Encode(image, stream);
}
}
}
}

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

@ -19,7 +19,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Performs the png encoding operation.
/// </summary>
internal sealed class PngEncoderCore
internal sealed class PngEncoderCore : IDisposable
{
/// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks.
@ -72,29 +72,44 @@ namespace ImageSharp.Formats
private int bytesPerPixel;
/// <summary>
/// The number of bytes per scanline
/// The number of bytes per scanline.
/// </summary>
private int bytesPerScanline;
/// <summary>
/// The previous scanline.
/// </summary>
private Buffer<byte> previousScanline;
/// <summary>
/// The raw scanline.
/// </summary>
private Buffer<byte> rawScanline;
/// <summary>
/// The filtered scanline result.
/// </summary>
private Buffer<byte> result;
/// <summary>
/// The buffer for the sub filter
/// </summary>
private byte[] sub;
private Buffer<byte> sub;
/// <summary>
/// The buffer for the up filter
/// </summary>
private byte[] up;
private Buffer<byte> up;
/// <summary>
/// The buffer for the average filter
/// </summary>
private byte[] average;
private Buffer<byte> average;
/// <summary>
/// The buffer for the paeth filter
/// </summary>
private byte[] paeth;
private Buffer<byte> paeth;
/// <summary>
/// The quality of output for images.
@ -212,6 +227,20 @@ namespace ImageSharp.Formats
stream.Flush();
}
/// <summary>
/// Disposes PngEncoderCore instance, disposing it's internal buffers.
/// </summary>
public void Dispose()
{
this.previousScanline?.Dispose();
this.rawScanline?.Dispose();
this.result?.Dispose();
this.sub?.Dispose();
this.up?.Dispose();
this.average?.Dispose();
this.paeth?.Dispose();
}
/// <summary>
/// Writes an integer to the byte array.
/// </summary>
@ -273,10 +302,11 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixels accessor.</param>
/// <param name="row">The row index.</param>
/// <param name="rawScanline">The raw scanline.</param>
private void CollectGrayscaleBytes<TPixel>(PixelAccessor<TPixel> pixels, int row, byte[] rawScanline)
private void CollectGrayscaleBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
// Copy the pixels across from the image.
// Reuse the chunk type buffer.
for (int x = 0; x < this.width; x++)
@ -291,11 +321,11 @@ namespace ImageSharp.Formats
{
if (i == 0)
{
rawScanline[offset] = luminance;
rawScanlineArray[offset] = luminance;
}
else
{
rawScanline[offset + i] = this.chunkTypeBuffer[3];
rawScanlineArray[offset + i] = this.chunkTypeBuffer[3];
}
}
}
@ -307,14 +337,18 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param>
/// <param name="row">The row index.</param>
/// <param name="rawScanline">The raw scanline.</param>
private void CollecTPixelBytes<TPixel>(PixelAccessor<TPixel> pixels, int row, byte[] rawScanline)
private void CollecTPixelBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
where TPixel : struct, IPixel<TPixel>
{
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
using (var pixelRow = new PixelArea<TPixel>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz))
BufferSpan<TPixel> rowSpan = pixels.GetRowSpan(row);
if (this.bytesPerPixel == 4)
{
PixelOperations<TPixel>.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width);
}
else
{
pixels.CopyTo(pixelRow, row);
PixelOperations<TPixel>.Instance.ToXyzBytes(rowSpan, this.rawScanline, this.width);
}
}
@ -325,89 +359,83 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param>
/// <param name="row">The row.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="rawScanline">The raw scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] EncodePixelRow<TPixel>(PixelAccessor<TPixel> pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result)
private Buffer<byte> EncodePixelRow<TPixel>(PixelAccessor<TPixel> pixels, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.pngColorType)
{
case PngColorType.Palette:
Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length);
Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length, this.rawScanline.Array, 0, this.rawScanline.Length);
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(pixels, row, rawScanline);
this.CollectGrayscaleBytes(pixels, row);
break;
default:
this.CollecTPixelBytes(pixels, row, rawScanline);
this.CollecTPixelBytes(pixels, row);
break;
}
return this.GetOptimalFilteredScanline(rawScanline, previousScanline, result);
return this.GetOptimalFilteredScanline();
}
/// <summary>
/// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
/// to be most compressible, using lowest total variation as proxy for compressibility.
/// </summary>
/// <param name="rawScanline">The raw scanline</param>
/// <param name="previousScanline">The previous scanline</param>
/// <param name="result">The filtered scanline result.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result)
private Buffer<byte> GetOptimalFilteredScanline()
{
var scanSpan = new BufferSpan<byte>(rawScanline);
var prevSpan = new BufferSpan<byte>(previousScanline);
BufferSpan<byte> scanSpan = this.rawScanline.Span;
BufferSpan<byte> prevSpan = this.previousScanline.Span;
// Palette images don't compress well with adaptive filtering.
if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
{
NoneFilter.Encode(rawScanline, result);
return result;
NoneFilter.Encode(this.rawScanline, this.result);
return this.result;
}
// This order, while different to the enumerated order is more likely to produce a smaller sum
// early on which shaves a couple of milliseconds off the processing time.
var upSpan = new BufferSpan<byte>(this.up);
BufferSpan<byte> upSpan = this.up.Span;
UpFilter.Encode(scanSpan, prevSpan, upSpan, this.bytesPerScanline);
int currentSum = this.CalculateTotalVariation(upSpan, int.MaxValue);
int lowestSum = currentSum;
result = this.up;
Buffer<byte> actualResult = this.up;
var paethSpan = new BufferSpan<byte>(this.paeth);
BufferSpan<byte> paethSpan = this.paeth.Span;
PaethFilter.Encode(scanSpan, prevSpan, paethSpan, this.bytesPerScanline, this.bytesPerPixel);
currentSum = this.CalculateTotalVariation(paethSpan, currentSum);
if (currentSum < lowestSum)
{
lowestSum = currentSum;
result = this.paeth;
actualResult = this.paeth;
}
var subSpan = new BufferSpan<byte>(this.sub);
BufferSpan<byte> subSpan = this.sub.Span;
SubFilter.Encode(scanSpan, subSpan, this.bytesPerScanline, this.bytesPerPixel);
currentSum = this.CalculateTotalVariation(subSpan, int.MaxValue);
if (currentSum < lowestSum)
{
lowestSum = currentSum;
result = this.sub;
actualResult = this.sub;
}
var averageSpan = new BufferSpan<byte>(this.average);
BufferSpan<byte> averageSpan = this.average.Span;
AverageFilter.Encode(scanSpan, prevSpan, averageSpan, this.bytesPerScanline, this.bytesPerPixel);
currentSum = this.CalculateTotalVariation(averageSpan, currentSum);
if (currentSum < lowestSum)
{
result = this.average;
actualResult = this.average;
}
return result;
return actualResult;
}
/// <summary>
@ -617,17 +645,18 @@ namespace ImageSharp.Formats
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.width * this.bytesPerPixel;
byte[] previousScanline = new byte[this.bytesPerScanline];
byte[] rawScanline = new byte[this.bytesPerScanline];
int resultLength = this.bytesPerScanline + 1;
byte[] result = new byte[resultLength];
this.previousScanline = new Buffer<byte>(this.bytesPerScanline);
this.rawScanline = new Buffer<byte>(this.bytesPerScanline);
this.result = new Buffer<byte>(resultLength);
if (this.pngColorType != PngColorType.Palette)
{
this.sub = new byte[resultLength];
this.up = new byte[resultLength];
this.average = new byte[resultLength];
this.paeth = new byte[resultLength];
this.sub = Buffer<byte>.CreateClean(resultLength);
this.up = Buffer<byte>.CreateClean(resultLength);
this.average = Buffer<byte>.CreateClean(resultLength);
this.paeth = Buffer<byte>.CreateClean(resultLength);
}
byte[] buffer;
@ -640,9 +669,10 @@ namespace ImageSharp.Formats
{
for (int y = 0; y < this.height; y++)
{
deflateStream.Write(this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result), 0, resultLength);
Buffer<byte> r = this.EncodePixelRow(pixels, y);
deflateStream.Write(r.Array, 0, resultLength);
Swap(ref rawScanline, ref previousScanline);
Swap(ref this.rawScanline, ref this.previousScanline);
}
}

Loading…
Cancel
Save