Browse Source

Remove index bounds checks from Png decoder

pull/712/head
James Jackson-South 8 years ago
parent
commit
8540033a6c
  1. 190
      src/ImageSharp/Formats/Png/PngDecoderCore.cs

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

@ -733,6 +733,9 @@ namespace SixLabors.ImageSharp.Formats.Png
? buffer.GetSpan()
: trimmed;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
switch (this.pngColorType)
{
case PngColorType.Grayscale:
@ -750,8 +753,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
pixel.PackFromRgb48(rgb48);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -760,12 +764,13 @@ 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)(scanlineSpan[x] * factor);
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -783,7 +788,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -791,14 +796,14 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba32 = default;
for (int x = 0; x < this.header.Width; x++)
{
byte luminance = (byte)(scanlineSpan[x] * factor);
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -820,17 +825,18 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.A = alpha;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
Rgba32 rgba32 = default;
int bps = this.bytesPerSample;
for (int x = 0; x < this.header.Width; x++)
{
int offset = x * this.bytesPerPixel;
byte luminance = scanlineSpan[offset];
byte alpha = scanlineSpan[offset + this.bytesPerSample];
byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bps);
rgba32.R = luminance;
rgba32.G = luminance;
@ -838,7 +844,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.A = alpha;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -846,7 +852,39 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
this.ProcessScanlineFromPalette(scanlineSpan, rowSpan);
ReadOnlySpan<Rgb24> palettePixels = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
ref Rgb24 palettePixelsRef = ref MemoryMarshal.GetReference(palettePixels);
if (this.paletteAlpha?.Length > 0)
{
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
// channel and we should try to read it.
Rgba32 rgba = default;
ref byte paletteAlphaRef = ref this.paletteAlpha[0];
for (int x = 0; x < this.header.Width; x++)
{
int index = Unsafe.Add(ref scanlineSpanRef, x);
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
rgba.A = this.paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
pixel.PackFromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
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 = Unsafe.Add(ref scanlineSpanRef, x);
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
break;
@ -862,8 +900,9 @@ namespace SixLabors.ImageSharp.Formats.Png
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;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -887,21 +926,22 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.A = rgb48.Equals(this.rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
ReadOnlySpan<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(scanlineSpan);
ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span);
for (int x = 0; x < this.header.Width; x++)
{
ref readonly Rgb24 rgb24 = ref rgb24Span[x];
ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x);
Rgba32 rgba32 = default;
rgba32.Rgb = rgb24;
rgba32.A = rgb24.Equals(this.rgb24Trans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -919,8 +959,9 @@ namespace SixLabors.ImageSharp.Formats.Png
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;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -955,6 +996,9 @@ namespace SixLabors.ImageSharp.Formats.Png
? buffer.GetSpan()
: trimmed;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
switch (this.pngColorType)
{
case PngColorType.Grayscale:
@ -974,7 +1018,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgb48.B = luminance;
pixel.PackFromRgb48(rgb48);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -983,13 +1027,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)(scanlineSpan[o] * factor);
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -1007,7 +1051,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -1015,14 +1059,14 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba32 = default;
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
byte luminance = (byte)(scanlineSpan[x] * factor);
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * factor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -1044,7 +1088,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.A = alpha;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -1053,15 +1097,15 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
int offset = x * this.bytesPerPixel;
byte luminance = scanlineSpan[offset];
byte alpha = scanlineSpan[offset + this.bytesPerSample];
byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + this.bytesPerSample);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
rgba32.A = alpha;
pixel.PackFromRgba32(rgba32);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -1070,20 +1114,22 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
Span<Rgb24> palettePixels = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
ref Rgb24 palettePixelsRef = ref MemoryMarshal.GetReference(palettePixels);
if (this.paletteAlpha?.Length > 0)
{
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
// channel and we should try to read it.
Rgba32 rgba = default;
ref byte paletteAlphaRef = ref this.paletteAlpha[0];
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
int index = scanlineSpan[o];
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue;
rgba.Rgb = palettePixels[index];
int index = Unsafe.Add(ref scanlineSpanRef, o);
rgba.A = this.paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -1091,11 +1137,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 = scanlineSpan[o];
rgba.Rgb = palettePixels[index];
int index = Unsafe.Add(ref scanlineSpanRef, o);
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -1119,7 +1165,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.A = rgb48.Equals(this.rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -1130,8 +1176,9 @@ namespace SixLabors.ImageSharp.Formats.Png
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;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -1142,13 +1189,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 = scanlineSpan[o];
rgba.G = scanlineSpan[o + this.bytesPerSample];
rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)];
rgba.R = Unsafe.Add(ref scanlineSpanRef, o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, o + this.bytesPerSample);
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * this.bytesPerSample));
rgba.A = this.rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -1156,12 +1203,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 = scanlineSpan[o];
rgba.G = scanlineSpan[o + this.bytesPerSample];
rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)];
rgba.R = Unsafe.Add(ref scanlineSpanRef, o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, o + this.bytesPerSample);
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * this.bytesPerSample));
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
@ -1179,8 +1226,9 @@ namespace SixLabors.ImageSharp.Formats.Png
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;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
@ -1188,13 +1236,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 = scanlineSpan[o];
rgba.G = scanlineSpan[o + this.bytesPerSample];
rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)];
rgba.A = scanlineSpan[o + (3 * this.bytesPerSample)];
rgba.R = Unsafe.Add(ref scanlineSpanRef, o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, o + this.bytesPerSample);
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * this.bytesPerSample));
rgba.A = Unsafe.Add(ref scanlineSpanRef, o + (3 * this.bytesPerSample));
pixel.PackFromRgba32(rgba);
rowSpan[x] = pixel;
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -1249,50 +1297,6 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
/// <summary>
/// Processes a scanline that uses a palette
/// </summary>
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="scanline">The defiltered scanline</param>
/// <param name="row">The current output image row</param>
private void ProcessScanlineFromPalette<TPixel>(ReadOnlySpan<byte> scanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel>
{
ReadOnlySpan<Rgb24> palettePixels = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
var color = default(TPixel);
if (this.paletteAlpha?.Length > 0)
{
Rgba32 rgba = default;
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
// channel and we should try to read it.
for (int x = 0; x < this.header.Width; x++)
{
int index = scanline[x];
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue;
rgba.Rgb = palettePixels[index];
color.PackFromRgba32(rgba);
row[x] = color;
}
}
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 = scanline[x];
rgba.Rgb = palettePixels[index];
color.PackFromRgba32(rgba);
row[x] = color;
}
}
}
/// <summary>
/// Reads a header chunk from the data.
/// </summary>

Loading…
Cancel
Save