Browse Source

Remove allocation when upscaling.

af/merge-core
James Jackson-South 8 years ago
parent
commit
8f37a41d58
  1. 296
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 5
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

296
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -341,25 +341,36 @@ namespace SixLabors.ImageSharp.Formats.Png
}
/// <summary>
/// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits.
/// Reads the least significant bits from the byte pair with the others set to 0.
/// </summary>
/// <param name="buffer">The source buffer</param>
/// <param name="offset">THe offset</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ReadByteLittleEndian(ReadOnlySpan<byte> buffer, int offset)
{
return (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF));
}
/// <summary>
/// Attempts to convert a byte array to a new array where each value in the original array is represented by the
/// specified number of bits.
/// </summary>
/// <param name="source">The bytes to convert from. Cannot be empty.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bits">The number of bits per value.</param>
/// <param name="buffer">The new array.</param>
/// <returns>The resulting <see cref="ReadOnlySpan{Byte}"/> array.</returns>
/// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
private static ReadOnlySpan<byte> ToArrayByBitsLength(ReadOnlySpan<byte> source, int bytesPerScanline, int bits)
private bool TryScaleUpTo8BitArray(ReadOnlySpan<byte> source, int bytesPerScanline, int bits, out IManagedByteBuffer buffer)
{
Guard.MustBeGreaterThan(source.Length, 0, nameof(source));
Guard.MustBeGreaterThan(bits, 0, nameof(bits));
if (bits >= 8)
{
return source;
buffer = null;
return false;
}
// TODO: We should be pooling this.
byte[] result = new byte[bytesPerScanline * 8 / bits];
buffer = this.MemoryAllocator.AllocateCleanManagedByteBuffer(bytesPerScanline * 8 / bits);
byte[] result = buffer.Array;
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
@ -369,26 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
result[resultOffset] = (byte)colorIndex;
resultOffset++;
}
}
return result;
}
/// <summary>
/// Reads the least significant bits from the byte pair with the others set to 0.
/// </summary>
/// <param name="buffer">The source buffer</param>
/// <param name="offset">THe offset</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ReadByteLittleEndian(ReadOnlySpan<byte> buffer, int offset)
{
return (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF));
return true;
}
/// <summary>
@ -669,11 +667,16 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
TPixel pixel = default;
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
// Trim the first marker byte from the buffer
ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
ReadOnlySpan<byte> trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
// Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent.
ReadOnlySpan<byte> scanlineSpan = this.TryScaleUpTo8BitArray(trimmed, this.bytesPerScanline, this.header.BitDepth, out IManagedByteBuffer buffer)
? buffer.GetSpan()
: trimmed;
switch (this.pngColorType)
{
@ -681,9 +684,6 @@ namespace SixLabors.ImageSharp.Formats.Png
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
// Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent.
ReadOnlySpan<byte> scanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
if (!this.hasTrans)
{
if (this.header.BitDepth == 16)
@ -691,12 +691,12 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgb48 rgb48 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
color.PackFromRgb48(rgb48);
rowSpan[x] = color;
pixel.PackFromRgb48(rgb48);
rowSpan[x] = pixel;
}
}
else
@ -705,12 +705,12 @@ namespace SixLabors.ImageSharp.Formats.Png
var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < this.header.Width; x++)
{
byte luminance = (byte)(scanline[x] * factor);
byte luminance = (byte)(scanlineSpan[x] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
}
@ -721,14 +721,14 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
@ -736,14 +736,14 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba32 = default;
for (int x = 0; x < this.header.Width; x++)
{
byte luminance = (byte)(scanline[x] * factor);
byte luminance = (byte)(scanlineSpan[x] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
}
@ -757,15 +757,15 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 4)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = alpha;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
@ -774,16 +774,16 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++)
{
int offset = x * this.bytesPerPixel;
byte luminance = scanlineBuffer[offset];
byte alpha = scanlineBuffer[offset + this.bytesPerSample];
byte luminance = scanlineSpan[offset];
byte alpha = scanlineSpan[offset + this.bytesPerSample];
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = alpha;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
@ -791,7 +791,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
this.ProcessScanlineFromPalette(scanlineBuffer, rowSpan);
this.ProcessScanlineFromPalette(scanlineSpan, rowSpan);
break;
@ -804,16 +804,16 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgb48 rgb48 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 6)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 4, 2));
color.PackFromRgb48(rgb48);
rowSpan[x] = color;
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2));
pixel.PackFromRgb48(rgb48);
rowSpan[x] = pixel;
}
}
else
{
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width);
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineSpan, rowSpan, this.header.Width);
}
}
else
@ -824,20 +824,20 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 6)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 4, 2));
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2));
rgba64.Rgb = rgb48;
rgba64.A = rgb48.Equals(this.rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
{
ReadOnlySpan<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(scanlineBuffer);
ReadOnlySpan<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(scanlineSpan);
for (int x = 0; x < this.header.Width; x++)
{
ref readonly Rgb24 rgb24 = ref rgb24Span[x];
@ -845,8 +845,8 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.Rgb = rgb24;
rgba32.A = rgb24.Equals(this.rgb24Trans) ? byte.MinValue : byte.MaxValue;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
}
@ -860,21 +860,23 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = 0, o = 0; x < this.header.Width; x++, o += 8)
{
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 4, 2));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 6, 2));
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 6, 2));
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
{
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(scanlineBuffer, rowSpan, this.header.Width);
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(scanlineSpan, rowSpan, this.header.Width);
}
break;
}
buffer?.Dispose();
}
/// <summary>
@ -888,10 +890,15 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
TPixel pixel = default;
// Trim the first marker byte from the buffer
ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
ReadOnlySpan<byte> trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
// Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent.
ReadOnlySpan<byte> scanlineSpan = this.TryScaleUpTo8BitArray(trimmed, this.bytesPerScanline, this.header.BitDepth, out IManagedByteBuffer buffer)
? buffer.GetSpan()
: trimmed;
switch (this.pngColorType)
{
@ -899,9 +906,6 @@ namespace SixLabors.ImageSharp.Formats.Png
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
// Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent.
ReadOnlySpan<byte> scanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
if (!this.hasTrans)
{
if (this.header.BitDepth == 16)
@ -909,13 +913,13 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgb48 rgb48 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
color.PackFromRgb48(rgb48);
rowSpan[x] = color;
pixel.PackFromRgb48(rgb48);
rowSpan[x] = pixel;
}
}
else
@ -924,13 +928,13 @@ namespace SixLabors.ImageSharp.Formats.Png
var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
byte luminance = (byte)(scanline[o] * factor);
byte luminance = (byte)(scanlineSpan[o] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
}
@ -941,14 +945,14 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
@ -956,14 +960,14 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba32 = default;
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
byte luminance = (byte)(scanline[x] * factor);
byte luminance = (byte)(scanlineSpan[x] * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
}
@ -977,15 +981,15 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
rgba64.A = alpha;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
@ -994,15 +998,15 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
int offset = x * this.bytesPerPixel;
byte luminance = scanlineBuffer[offset];
byte alpha = scanlineBuffer[offset + this.bytesPerSample];
byte luminance = scanlineSpan[offset];
byte alpha = scanlineSpan[offset + this.bytesPerSample];
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = alpha;
color.PackFromRgba32(rgba32);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
}
}
@ -1010,8 +1014,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
Span<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
Span<Rgb24> palettePixels = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
if (this.paletteAlpha?.Length > 0)
{
@ -1020,12 +1023,12 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
int index = newScanline[o];
int index = scanlineSpan[o];
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue;
rgba.Rgb = pal[index];
rgba.Rgb = palettePixels[index];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
}
}
else
@ -1033,11 +1036,11 @@ namespace SixLabors.ImageSharp.Formats.Png
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
int index = newScanline[o];
rgba.Rgb = pal[index];
int index = scanlineSpan[o];
rgba.Rgb = palettePixels[index];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
}
}
@ -1053,15 +1056,15 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 6)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 4, 2));
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2));
rgba64.Rgb = rgb48;
rgba64.A = rgb48.Equals(this.rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
@ -1069,11 +1072,11 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgb48 rgb48 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 6)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 4, 2));
color.PackFromRgb48(rgb48);
rowSpan[x] = color;
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2));
pixel.PackFromRgb48(rgb48);
rowSpan[x] = pixel;
}
}
}
@ -1084,13 +1087,13 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = scanlineBuffer[o];
rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
rgba.R = scanlineSpan[o];
rgba.G = scanlineSpan[o + this.bytesPerSample];
rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)];
rgba.A = this.rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
}
}
else
@ -1098,12 +1101,12 @@ namespace SixLabors.ImageSharp.Formats.Png
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = scanlineBuffer[o];
rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
rgba.R = scanlineSpan[o];
rgba.G = scanlineSpan[o + this.bytesPerSample];
rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
}
}
}
@ -1117,12 +1120,12 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba64 rgba64 = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 8)
{
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o, 2));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 2, 2));
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 4, 2));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineBuffer.Slice(o + 6, 2));
color.PackFromRgba64(rgba64);
rowSpan[x] = color;
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 6, 2));
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
}
}
else
@ -1130,18 +1133,20 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba = default;
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = scanlineBuffer[o];
rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
rgba.A = scanlineBuffer[o + (3 * this.bytesPerSample)];
rgba.R = scanlineSpan[o];
rgba.G = scanlineSpan[o + this.bytesPerSample];
rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)];
rgba.A = scanlineSpan[o + (3 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
}
}
break;
}
buffer?.Dispose();
}
/// <summary>
@ -1193,13 +1198,12 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Processes a scanline that uses a palette
/// </summary>
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param>
/// <param name="scanline">The defiltered scanline</param>
/// <param name="row">Thecurrent output image row</param>
private void ProcessScanlineFromPalette<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> row)
private void ProcessScanlineFromPalette<TPixel>(ReadOnlySpan<byte> scanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel>
{
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
ReadOnlySpan<Rgb24> palettePixels = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
var color = default(TPixel);
if (this.paletteAlpha?.Length > 0)
@ -1210,9 +1214,9 @@ namespace SixLabors.ImageSharp.Formats.Png
// channel and we should try to read it.
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x];
int index = scanline[x];
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue;
rgba.Rgb = pal[index];
rgba.Rgb = palettePixels[index];
color.PackFromRgba32(rgba);
row[x] = color;
@ -1220,13 +1224,13 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
// TODO: We should have PackFromRgb24.
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x];
int index = scanline[x];
rgba.Rgb = pal[index];
rgba.Rgb = palettePixels[index];
color.PackFromRgba32(rgba);
row[x] = color;

5
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -44,14 +44,14 @@ namespace SixLabors.ImageSharp.Tests
// IDAT
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00,
0x00, 0x04, 0x00, 0x01,
// IDAT CRC
0x5C, 0xCD, 0xFF, 0x69,
// IEND
0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0x4E, 0x44,
// IEND CRC
0xAE, 0x42, 0x60, 0x82
};
@ -76,7 +76,6 @@ namespace SixLabors.ImageSharp.Tests
TestImages.Png.VimImage2,
};
public static readonly string[] TestImages48Bpp =
{
TestImages.Png.Rgb48Bpp,

Loading…
Cancel
Save