Browse Source

Faster png palette encoding.

pull/1138/head
James Jackson-South 7 years ago
parent
commit
01efb09758
  1. 39
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  2. 1
      src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs

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

@ -555,51 +555,46 @@ namespace SixLabors.ImageSharp.Formats.Png
// Grab the palette and write it to the stream. // Grab the palette and write it to the stream.
ReadOnlySpan<TPixel> palette = quantized.Palette.Span; ReadOnlySpan<TPixel> palette = quantized.Palette.Span;
int paletteLength = Math.Min(palette.Length, QuantizerConstants.MaxColors); int paletteLength = palette.Length;
int colorTableLength = paletteLength * 3; int colorTableLength = paletteLength * Unsafe.SizeOf<Rgb24>();
bool anyAlpha = false; bool hasAlpha = false;
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength)) using IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength);
using (IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength)) using IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength);
{
ref byte colorTableRef = ref MemoryMarshal.GetReference(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());
ReadOnlySpan<byte> quantizedSpan = quantized.GetPixelBufferSpan();
Rgba32 rgba = default; // Bulk convert our palette to RGBA to allow assignment to tables.
// Palette length maxes out at 256 so safe to stackalloc.
Span<Rgba32> rgbaPaletteSpan = stackalloc Rgba32[palette.Length];
PixelOperations<TPixel>.Instance.ToRgba32(quantized.Configuration, quantized.Palette.Span, rgbaPaletteSpan);
ref Rgba32 rgbaPaletteRef = ref MemoryMarshal.GetReference(rgbaPaletteSpan);
// Loop, assign, and extract alpha values from the palette.
for (int i = 0; i < paletteLength; i++) for (int i = 0; i < paletteLength; i++)
{ {
if (quantizedSpan.IndexOf((byte)i) > -1) Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, i);
{
int offset = i * 3;
palette[i].ToRgba32(ref rgba);
byte alpha = rgba.A; byte alpha = rgba.A;
Unsafe.Add(ref colorTableRef, offset) = rgba.R; Unsafe.Add(ref colorTableRef, i) = rgba.Rgb;
Unsafe.Add(ref colorTableRef, offset + 1) = rgba.G;
Unsafe.Add(ref colorTableRef, offset + 2) = rgba.B;
if (alpha > this.options.Threshold) if (alpha > this.options.Threshold)
{ {
alpha = byte.MaxValue; alpha = byte.MaxValue;
} }
anyAlpha = anyAlpha || alpha < byte.MaxValue; hasAlpha = hasAlpha || alpha < byte.MaxValue;
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.Array, 0, colorTableLength);
// Write the transparency data // Write the transparency data
if (anyAlpha) if (hasAlpha)
{ {
this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength); this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
} }
} }
}
/// <summary> /// <summary>
/// Writes the physical dimension information to the stream. /// Writes the physical dimension information to the stream.

1
src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs

@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
internal IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory<TPixel> palette) internal IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory<TPixel> palette)
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeLessThanOrEqualTo(palette.Length, QuantizerConstants.MaxColors, nameof(palette));
Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.MustBeGreaterThan(height, 0, nameof(height));

Loading…
Cancel
Save