Browse Source

Optimize x-bit scanline packing.

pull/712/head
James Jackson-South 7 years ago
parent
commit
7b692fd15a
  1. 2
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  2. 8
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 2
      src/ImageSharp/Formats/Png/PngEncoder.cs
  4. 33
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  5. 4
      src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs

2
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Gets the filter method.
/// </summary>
PngFilterMethod FilterMethod { get; }
PngFilterMethod? FilterMethod { get; }
/// <summary>
/// Gets the compression level 1-9.

8
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++;
}
}

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

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Gets or sets the filter method.
/// </summary>
public PngFilterMethod FilterMethod { get; set; } = PngFilterMethod.Paeth;
public PngFilterMethod? FilterMethod { get; set; }
/// <summary>
/// Gets or sets the compression level 1-9.

33
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<TPixel>().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
}
/// <summary>
/// Packs the given 8 bit array into and array of <paramref name="bits"/> depths.
/// </summary>
/// <param name="source">The source span in 8 bits.</param>
/// <param name="result">The resultant span in <paramref name="bits"/>.</param>
/// <param name="bits">The bit depth.</param>
private void ScaleDownFrom8BitArray(ReadOnlySpan<byte> source, Span<byte> 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;
}
}

4
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 <see cref="QuantizedFrame{TPixel}"/>.
/// </summary>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Span<byte> GetPixelSpan() => this.pixels.GetSpan();
/// <summary>
@ -65,6 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Span<byte> GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width);
/// <inheritdoc/>

Loading…
Cancel
Save