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;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {

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

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

Loading…
Cancel
Save