|
|
@ -9,7 +9,7 @@ namespace ImageSharp.Formats |
|
|
using System.Buffers; |
|
|
using System.Buffers; |
|
|
using System.IO; |
|
|
using System.IO; |
|
|
using System.Linq; |
|
|
using System.Linq; |
|
|
|
|
|
using System.Runtime.CompilerServices; |
|
|
using ImageSharp.PixelFormats; |
|
|
using ImageSharp.PixelFormats; |
|
|
|
|
|
|
|
|
using Quantizers; |
|
|
using Quantizers; |
|
|
@ -71,6 +71,11 @@ namespace ImageSharp.Formats |
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
private int bytesPerPixel; |
|
|
private int bytesPerPixel; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The number of bytes per scanline
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private int bytesPerScanline; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The buffer for the sub filter
|
|
|
/// The buffer for the sub filter
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
@ -177,7 +182,7 @@ namespace ImageSharp.Formats |
|
|
|
|
|
|
|
|
this.bytesPerPixel = this.CalculateBytesPerPixel(); |
|
|
this.bytesPerPixel = this.CalculateBytesPerPixel(); |
|
|
|
|
|
|
|
|
PngHeader header = new PngHeader |
|
|
var header = new PngHeader |
|
|
{ |
|
|
{ |
|
|
Width = image.Width, |
|
|
Width = image.Width, |
|
|
Height = image.Height, |
|
|
Height = image.Height, |
|
|
@ -307,7 +312,7 @@ namespace ImageSharp.Formats |
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
{ |
|
|
{ |
|
|
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
|
|
|
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
|
|
|
using (PixelArea<TPixel> pixelRow = new PixelArea<TPixel>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) |
|
|
using (var pixelRow = new PixelArea<TPixel>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) |
|
|
{ |
|
|
{ |
|
|
pixels.CopyTo(pixelRow, row); |
|
|
pixels.CopyTo(pixelRow, row); |
|
|
} |
|
|
} |
|
|
@ -354,6 +359,11 @@ namespace ImageSharp.Formats |
|
|
/// <returns>The <see cref="T:byte[]"/></returns>
|
|
|
/// <returns>The <see cref="T:byte[]"/></returns>
|
|
|
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result) |
|
|
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result) |
|
|
{ |
|
|
{ |
|
|
|
|
|
var scanSpan = new BufferSpan<byte>(rawScanline); |
|
|
|
|
|
var prevSpan = new BufferSpan<byte>(previousScanline); |
|
|
|
|
|
ref byte scanPointer = ref scanSpan.DangerousGetPinnableReference(); |
|
|
|
|
|
ref byte prevPointer = ref prevSpan.DangerousGetPinnableReference(); |
|
|
|
|
|
|
|
|
// Palette images don't compress well with adaptive filtering.
|
|
|
// Palette images don't compress well with adaptive filtering.
|
|
|
if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) |
|
|
if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) |
|
|
{ |
|
|
{ |
|
|
@ -363,13 +373,18 @@ namespace ImageSharp.Formats |
|
|
|
|
|
|
|
|
// This order, while different to the enumerated order is more likely to produce a smaller sum
|
|
|
// 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.
|
|
|
// early on which shaves a couple of milliseconds off the processing time.
|
|
|
UpFilter.Encode(rawScanline, previousScanline, this.up); |
|
|
var upSpan = new BufferSpan<byte>(this.up); |
|
|
int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue); |
|
|
ref byte upPointer = ref upSpan.DangerousGetPinnableReference(); |
|
|
|
|
|
UpFilter.Encode(ref scanPointer, ref prevPointer, ref upPointer, this.bytesPerScanline); |
|
|
|
|
|
|
|
|
|
|
|
int currentSum = this.CalculateTotalVariation(ref upPointer, int.MaxValue); |
|
|
int lowestSum = currentSum; |
|
|
int lowestSum = currentSum; |
|
|
result = this.up; |
|
|
result = this.up; |
|
|
|
|
|
|
|
|
PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel); |
|
|
var paethSpan = new BufferSpan<byte>(this.paeth); |
|
|
currentSum = this.CalculateTotalVariation(this.paeth, currentSum); |
|
|
ref byte paethPointer = ref paethSpan.DangerousGetPinnableReference(); |
|
|
|
|
|
PaethFilter.Encode(ref scanPointer, ref prevPointer, ref paethPointer, this.bytesPerScanline, this.bytesPerPixel); |
|
|
|
|
|
currentSum = this.CalculateTotalVariation(ref paethPointer, currentSum); |
|
|
|
|
|
|
|
|
if (currentSum < lowestSum) |
|
|
if (currentSum < lowestSum) |
|
|
{ |
|
|
{ |
|
|
@ -377,8 +392,10 @@ namespace ImageSharp.Formats |
|
|
result = this.paeth; |
|
|
result = this.paeth; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel); |
|
|
var subSpan = new BufferSpan<byte>(this.sub); |
|
|
currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue); |
|
|
ref byte subPointer = ref subSpan.DangerousGetPinnableReference(); |
|
|
|
|
|
SubFilter.Encode(ref scanPointer, ref subPointer, this.bytesPerScanline, this.bytesPerPixel); |
|
|
|
|
|
currentSum = this.CalculateTotalVariation(ref subPointer, int.MaxValue); |
|
|
|
|
|
|
|
|
if (currentSum < lowestSum) |
|
|
if (currentSum < lowestSum) |
|
|
{ |
|
|
{ |
|
|
@ -386,8 +403,10 @@ namespace ImageSharp.Formats |
|
|
result = this.sub; |
|
|
result = this.sub; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel); |
|
|
var averageSpan = new BufferSpan<byte>(this.average); |
|
|
currentSum = this.CalculateTotalVariation(this.average, currentSum); |
|
|
ref byte averagePointer = ref averageSpan.DangerousGetPinnableReference(); |
|
|
|
|
|
AverageFilter.Encode(ref scanPointer, ref prevPointer, ref averagePointer, this.bytesPerScanline, this.bytesPerPixel); |
|
|
|
|
|
currentSum = this.CalculateTotalVariation(ref averagePointer, currentSum); |
|
|
|
|
|
|
|
|
if (currentSum < lowestSum) |
|
|
if (currentSum < lowestSum) |
|
|
{ |
|
|
{ |
|
|
@ -404,13 +423,14 @@ namespace ImageSharp.Formats |
|
|
/// <param name="scanline">The scanline bytes</param>
|
|
|
/// <param name="scanline">The scanline bytes</param>
|
|
|
/// <param name="lastSum">The last variation sum</param>
|
|
|
/// <param name="lastSum">The last variation sum</param>
|
|
|
/// <returns>The <see cref="int"/></returns>
|
|
|
/// <returns>The <see cref="int"/></returns>
|
|
|
private int CalculateTotalVariation(byte[] scanline, int lastSum) |
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
|
|
|
private int CalculateTotalVariation(ref byte scanline, int lastSum) |
|
|
{ |
|
|
{ |
|
|
int sum = 0; |
|
|
int sum = 0; |
|
|
|
|
|
|
|
|
for (int i = 1; i < scanline.Length; i++) |
|
|
for (int i = 1; i < this.bytesPerScanline; i++) |
|
|
{ |
|
|
{ |
|
|
byte v = scanline[i]; |
|
|
ref byte v = ref Unsafe.Add(ref scanline, i); |
|
|
sum += v < 128 ? v : 256 - v; |
|
|
sum += v < 128 ? v : 256 - v; |
|
|
|
|
|
|
|
|
// No point continuing if we are larger.
|
|
|
// No point continuing if we are larger.
|
|
|
@ -601,10 +621,10 @@ namespace ImageSharp.Formats |
|
|
private void WriteDataChunks<TPixel>(PixelAccessor<TPixel> pixels, Stream stream) |
|
|
private void WriteDataChunks<TPixel>(PixelAccessor<TPixel> pixels, Stream stream) |
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
{ |
|
|
{ |
|
|
int bytesPerScanline = this.width * this.bytesPerPixel; |
|
|
this.bytesPerScanline = this.width * this.bytesPerPixel; |
|
|
byte[] previousScanline = new byte[bytesPerScanline]; |
|
|
byte[] previousScanline = new byte[this.bytesPerScanline]; |
|
|
byte[] rawScanline = new byte[bytesPerScanline]; |
|
|
byte[] rawScanline = new byte[this.bytesPerScanline]; |
|
|
int resultLength = bytesPerScanline + 1; |
|
|
int resultLength = this.bytesPerScanline + 1; |
|
|
byte[] result = new byte[resultLength]; |
|
|
byte[] result = new byte[resultLength]; |
|
|
|
|
|
|
|
|
if (this.pngColorType != PngColorType.Palette) |
|
|
if (this.pngColorType != PngColorType.Palette) |
|
|
@ -621,7 +641,7 @@ namespace ImageSharp.Formats |
|
|
try |
|
|
try |
|
|
{ |
|
|
{ |
|
|
memoryStream = new MemoryStream(); |
|
|
memoryStream = new MemoryStream(); |
|
|
using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) |
|
|
using (var deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) |
|
|
{ |
|
|
{ |
|
|
for (int y = 0; y < this.height; y++) |
|
|
for (int y = 0; y < this.height; y++) |
|
|
{ |
|
|
{ |
|
|
|