diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 0acf0f4bf9..8ec3bd9504 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/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 palettePixels = MemoryMarshal.Cast(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 rgb24Span = MemoryMarshal.Cast(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 palettePixels = MemoryMarshal.Cast(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 } } - /// - /// Processes a scanline that uses a palette - /// - /// The type of pixel we are expanding to - /// The defiltered scanline - /// The current output image row - private void ProcessScanlineFromPalette(ReadOnlySpan scanline, Span row) - where TPixel : struct, IPixel - { - ReadOnlySpan palettePixels = MemoryMarshal.Cast(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; - } - } - } - /// /// Reads a header chunk from the data. ///