diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
index 77bc9f7a0..7e5a9fa6b 100644
--- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Gets the filter method.
///
- PngFilterMethod FilterMethod { get; }
+ PngFilterMethod? FilterMethod { get; }
///
/// Gets the compression level 1-9.
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index c52f97494..0acf0f4bf 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -396,18 +396,18 @@ namespace SixLabors.ImageSharp.Formats.Png
}
buffer = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline * 8 / bits, AllocationOptions.Clean);
- byte[] result = buffer.Array;
+ ref byte sourceRef = ref MemoryMarshal.GetReference(source);
+ ref byte resultRef = ref buffer.Array[0];
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
for (int i = 0; i < bytesPerScanline; i++)
{
- byte b = source[i];
+ byte b = Unsafe.Add(ref sourceRef, i);
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
- result[resultOffset] = (byte)colorIndex;
-
+ Unsafe.Add(ref resultRef, resultOffset) = (byte)colorIndex;
resultOffset++;
}
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs
index f47a6518f..96e97a305 100644
--- a/src/ImageSharp/Formats/Png/PngEncoder.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoder.cs
@@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Gets or sets the filter method.
///
- public PngFilterMethod FilterMethod { get; set; } = PngFilterMethod.Paeth;
+ public PngFilterMethod? FilterMethod { get; set; }
///
/// Gets or sets the compression level 1-9.
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index bc7f1906f..48d611f58 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -4,6 +4,8 @@
using System;
using System.Buffers.Binary;
using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Png.Filters;
@@ -162,8 +164,10 @@ namespace SixLabors.ImageSharp.Formats.Png
this.pngBitDepth = options.BitDepth;
this.pngColorType = options.ColorType;
- // Palette compresses better with none and spec recommends it.
- this.pngFilterMethod = options.ColorType.Equals(PngColorType.Palette) ? PngFilterMethod.None : options.FilterMethod;
+ // Specification recommends default filter method None for paletted images and Paeth for others.
+ this.pngFilterMethod = options.FilterMethod ?? (options.ColorType.Equals(PngColorType.Palette)
+ ? PngFilterMethod.None
+ : PngFilterMethod.Paeth);
this.compressionLevel = options.CompressionLevel;
this.gamma = options.Gamma;
this.quantizer = options.Quantizer;
@@ -212,9 +216,11 @@ namespace SixLabors.ImageSharp.Formats.Png
// Create quantized frame returning the palette and set the bit depth.
quantized = this.quantizer.CreateFrameQuantizer().QuantizeFrame(image.Frames.RootFrame);
byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
+ bits = Math.Max(bits, quantizedBits);
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
- bits = Math.Max(bits, quantizedBits);
+ // We check again for the bit depth as the bit depth of the color palette from a given quantizer might not
+ // be within the acceptable range.
if (bits == 3)
{
bits = 4;
@@ -228,6 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
+ // TODO: Get the correct bit depth for grayscale images.
this.bitDepth = (byte)(this.use16Bit ? 16 : 8);
}
@@ -416,13 +423,13 @@ namespace SixLabors.ImageSharp.Formats.Png
{
case PngColorType.Palette:
- int stride = this.rawScanline.Length();
if (this.bitDepth < 8)
{
this.ScaleDownFrom8BitArray(quantized.GetRowSpan(row), this.rawScanline.GetSpan(), this.bitDepth);
}
else
{
+ int stride = this.rawScanline.Length();
quantized.GetPixelSpan().Slice(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
}
@@ -841,12 +848,16 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(this.buffer, 0, 4); // write the crc
}
+ ///
+ /// Packs the given 8 bit array into and array of depths.
+ ///
+ /// The source span in 8 bits.
+ /// The resultant span in .
+ /// The bit depth.
private void ScaleDownFrom8BitArray(ReadOnlySpan source, Span result, int bits)
{
- if (bits >= 8)
- {
- return;
- }
+ ref byte sourceRef = ref MemoryMarshal.GetReference(source);
+ ref byte resultRef = ref MemoryMarshal.GetReference(result);
byte mask = (byte)(0xFF >> (8 - bits));
byte shift0 = (byte)(8 - bits);
@@ -856,13 +867,13 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int i = 0; i < source.Length; i++)
{
- int value = source[i] & mask;
+ int value = Unsafe.Add(ref sourceRef, i) & mask;
v |= value << shift;
if (shift == 0)
{
shift = shift0;
- result[resultOffset] = (byte)v;
+ Unsafe.Add(ref resultRef, resultOffset) = (byte)v;
resultOffset++;
v = 0;
}
@@ -874,7 +885,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (shift != shift0)
{
- result[resultOffset] = (byte)v;
+ Unsafe.Add(ref resultRef, resultOffset) = (byte)v;
}
}
diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs
index 61ea342a8..38862ef44 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs
@@ -3,7 +3,7 @@
using System;
using System.Buffers;
-
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -57,6 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Gets the pixels of this .
///
/// The
+ [MethodImpl(InliningOptions.ShortMethod)]
public Span GetPixelSpan() => this.pixels.GetSpan();
///
@@ -65,6 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
/// The row.
/// The
+ [MethodImpl(InliningOptions.ShortMethod)]
public Span GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width);
///