Browse Source

Migrate png encoder

pull/1681/head
James Jackson-South 5 years ago
parent
commit
5bc3850d09
  1. 1
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  2. 95
      src/ImageSharp/Formats/Png/PngEncoderCore.cs

1
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp
{

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

@ -80,32 +80,32 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// The raw data of previous scanline.
/// </summary>
private IManagedByteBuffer previousScanline;
private IMemoryOwner<byte> previousScanline;
/// <summary>
/// The raw data of current scanline.
/// </summary>
private IManagedByteBuffer currentScanline;
private IMemoryOwner<byte> currentScanline;
/// <summary>
/// The common buffer for the filters.
/// </summary>
private IManagedByteBuffer filterBuffer;
private IMemoryOwner<byte> filterBuffer;
/// <summary>
/// The ext buffer for the sub filter, <see cref="PngFilterMethod.Adaptive"/>.
/// </summary>
private IManagedByteBuffer subFilter;
private IMemoryOwner<byte> subFilter;
/// <summary>
/// The ext buffer for the average filter, <see cref="PngFilterMethod.Adaptive"/>.
/// </summary>
private IManagedByteBuffer averageFilter;
private IMemoryOwner<byte> averageFilter;
/// <summary>
/// The ext buffer for the Paeth filter, <see cref="PngFilterMethod.Adaptive"/>.
/// </summary>
private IManagedByteBuffer paethFilter;
private IMemoryOwner<byte> paethFilter;
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderCore" /> class.
@ -278,21 +278,17 @@ namespace SixLabors.ImageSharp.Formats.Png
else
{
// 1, 2, and 4 bit grayscale
using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(
rowSpan.Length,
AllocationOptions.Clean))
{
int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(this.bitDepth) - 1);
Span<byte> tempSpan = temp.GetSpan();
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
rowSpan,
tempSpan,
rowSpan.Length);
PngEncoderHelpers.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
using IMemoryOwner<byte> temp = this.memoryAllocator.Allocate<byte>(rowSpan.Length, AllocationOptions.Clean);
int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(this.bitDepth) - 1);
Span<byte> tempSpan = temp.GetSpan();
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
rowSpan,
tempSpan,
rowSpan.Length);
PngEncoderHelpers.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
}
}
@ -453,7 +449,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Apply filter for the raw scanline.
/// </summary>
private IManagedByteBuffer FilterPixelBytes()
private IMemoryOwner<byte> FilterPixelBytes()
{
switch (this.options.FilterMethod)
{
@ -490,8 +486,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="rowSpan">The row span.</param>
/// <param name="quantized">The quantized pixels. Can be null.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="IManagedByteBuffer"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, IndexedImageFrame<TPixel> quantized, int row)
/// <returns>The <see cref="IMemoryOwner{Byte}"/></returns>
private IMemoryOwner<byte> EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, IndexedImageFrame<TPixel> quantized, int row)
where TPixel : unmanaged, IPixel<TPixel>
{
this.CollectPixelBytes(rowSpan, quantized, row);
@ -502,7 +498,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Encodes the indexed pixel data (with palette) for Adam7 interlaced mode.
/// </summary>
/// <param name="rowSpan">The row span.</param>
private IManagedByteBuffer EncodeAdam7IndexedPixelRow(ReadOnlySpan<byte> rowSpan)
private IMemoryOwner<byte> EncodeAdam7IndexedPixelRow(ReadOnlySpan<byte> rowSpan)
{
// CollectPixelBytes
if (this.bitDepth < 8)
@ -522,7 +518,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// to be most compressible, using lowest total variation as proxy for compressibility.
/// </summary>
/// <returns>The <see cref="T:byte[]"/></returns>
private IManagedByteBuffer GetOptimalFilteredScanline()
private IMemoryOwner<byte> GetOptimalFilteredScanline()
{
// Palette images don't compress well with adaptive filtering.
if (this.options.ColorType == PngColorType.Palette || this.bitDepth < 8)
@ -543,7 +539,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// That way the above comment would actually be true. It used to be anyway...
// If we could use SIMD for none branching filters we could really speed it up.
int lowestSum = currentSum;
IManagedByteBuffer actualResult = this.filterBuffer;
IMemoryOwner<byte> actualResult = this.filterBuffer;
PaethFilter.Encode(scanSpan, prevSpan, this.paethFilter.GetSpan(), this.bytesPerPixel, out currentSum);
@ -612,8 +608,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int colorTableLength = paletteLength * Unsafe.SizeOf<Rgb24>();
bool hasAlpha = false;
using IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength);
using IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength);
using IMemoryOwner<byte> colorTable = this.memoryAllocator.Allocate<byte>(colorTableLength);
using IMemoryOwner<byte> alphaTable = this.memoryAllocator.Allocate<byte>(paletteLength);
ref Rgb24 colorTableRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, Rgb24>(colorTable.GetSpan()));
ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan());
@ -640,12 +636,12 @@ namespace SixLabors.ImageSharp.Formats.Png
Unsafe.Add(ref alphaTableRef, i) = alpha;
}
this.WriteChunk(stream, PngChunkType.Palette, colorTable.Array, 0, colorTableLength);
this.WriteChunk(stream, PngChunkType.Palette, colorTable.GetSpan(), 0, colorTableLength);
// Write the transparency data
if (hasAlpha)
{
this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.GetSpan(), 0, paletteLength);
}
}
@ -938,9 +934,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.previousScanline?.Dispose();
this.currentScanline?.Dispose();
this.filterBuffer?.Dispose();
this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline, AllocationOptions.Clean);
this.currentScanline = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline, AllocationOptions.Clean);
this.filterBuffer = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
this.previousScanline = this.memoryAllocator.Allocate<byte>(bytesPerScanline, AllocationOptions.Clean);
this.currentScanline = this.memoryAllocator.Allocate<byte>(bytesPerScanline, AllocationOptions.Clean);
this.filterBuffer = this.memoryAllocator.Allocate<byte>(resultLength, AllocationOptions.Clean);
}
/// <summary>
@ -952,9 +948,9 @@ namespace SixLabors.ImageSharp.Formats.Png
{
int resultLength = this.filterBuffer.Length();
this.subFilter = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
this.averageFilter = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
this.paethFilter = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
this.subFilter = this.memoryAllocator.Allocate<byte>(resultLength, AllocationOptions.Clean);
this.averageFilter = this.memoryAllocator.Allocate<byte>(resultLength, AllocationOptions.Clean);
this.paethFilter = this.memoryAllocator.Allocate<byte>(resultLength, AllocationOptions.Clean);
}
}
@ -974,10 +970,10 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int y = 0; y < this.height; y++)
{
IManagedByteBuffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), quantized, y);
deflateStream.Write(r.Array, 0, resultLength);
IMemoryOwner<byte> r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), quantized, y);
deflateStream.Write(r.GetSpan(), 0, resultLength);
IManagedByteBuffer temp = this.currentScanline;
IMemoryOwner<byte> temp = this.currentScanline;
this.currentScanline = this.previousScanline;
this.previousScanline = temp;
}
@ -1027,10 +1023,10 @@ namespace SixLabors.ImageSharp.Formats.Png
// encode data
// note: quantized parameter not used
// note: row parameter not used
IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan<TPixel>)destSpan, null, -1);
deflateStream.Write(r.Array, 0, resultLength);
IMemoryOwner<byte> r = this.EncodePixelRow((ReadOnlySpan<TPixel>)destSpan, null, -1);
deflateStream.Write(r.GetSpan(), 0, resultLength);
IManagedByteBuffer temp = this.currentScanline;
IMemoryOwner<byte> temp = this.currentScanline;
this.currentScanline = this.previousScanline;
this.previousScanline = temp;
}
@ -1080,10 +1076,10 @@ namespace SixLabors.ImageSharp.Formats.Png
}
// encode data
IManagedByteBuffer r = this.EncodeAdam7IndexedPixelRow(destSpan);
deflateStream.Write(r.Array, 0, resultLength);
IMemoryOwner<byte> r = this.EncodeAdam7IndexedPixelRow(destSpan);
deflateStream.Write(r.GetSpan(), 0, resultLength);
IManagedByteBuffer temp = this.currentScanline;
IMemoryOwner<byte> temp = this.currentScanline;
this.currentScanline = this.previousScanline;
this.previousScanline = temp;
}
@ -1103,7 +1099,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="type">The type of chunk to write.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
private void WriteChunk(Stream stream, PngChunkType type, byte[] data) => this.WriteChunk(stream, type, data, 0, data?.Length ?? 0);
private void WriteChunk(Stream stream, PngChunkType type, Span<byte> data)
=> this.WriteChunk(stream, type, data, 0, data.Length);
/// <summary>
/// Writes a chunk of a specified length to the stream at the given offset.
@ -1113,7 +1110,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
/// <param name="offset">The position to offset the data at.</param>
/// <param name="length">The of the data to write.</param>
private void WriteChunk(Stream stream, PngChunkType type, byte[] data, int offset, int length)
private void WriteChunk(Stream stream, PngChunkType type, Span<byte> data, int offset, int length)
{
BinaryPrimitives.WriteInt32BigEndian(this.buffer, length);
BinaryPrimitives.WriteUInt32BigEndian(this.buffer.AsSpan(4, 4), (uint)type);
@ -1126,7 +1123,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
stream.Write(data, offset, length);
crc = Crc32.Calculate(crc, data.AsSpan(offset, length));
crc = Crc32.Calculate(crc, data.Slice(offset, length));
}
BinaryPrimitives.WriteUInt32BigEndian(this.buffer, crc);

Loading…
Cancel
Save