From 5ed6f24943b697d2c354d762ebaaa6aecee27274 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 23 Oct 2023 21:54:53 +1000 Subject: [PATCH] Reintroduce scanline optimizations --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 20 +++++- .../Formats/Png/PngFrameMetadata.cs | 2 +- .../Formats/Png/PngScanlineProcessor.cs | 65 ++++++++++++------- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index f84d936c81..8484fd0c6d 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -567,7 +567,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals if (frameControl is { } control) { PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngFrameMetadata(); - frameMetadata.FromChunk(control); + frameMetadata.FromChunk(in control); } this.bytesPerPixel = this.CalculateBytesPerPixel(); @@ -837,7 +837,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals } Span rowSpan = imageBuffer.DangerousGetRowSpan(currentRow); - this.ProcessInterlacedDefilteredScanline(frameControl, this.scanline.GetSpan(), rowSpan, pngMetadata, pixelOffset: Adam7.FirstColumn[pass], increment: Adam7.ColumnIncrement[pass]); + this.ProcessInterlacedDefilteredScanline( + frameControl, + this.scanline.GetSpan(), + rowSpan, + pngMetadata, + pixelOffset: Adam7.FirstColumn[pass], + increment: Adam7.ColumnIncrement[pass]); this.SwapScanlineBuffers(); @@ -935,6 +941,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessRgbaScanline( + this.configuration, this.header.BitDepth, in frameControl, scanlineSpan, @@ -961,7 +968,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// The png metadata. /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. - private void ProcessInterlacedDefilteredScanline(in FrameControl frameControl, ReadOnlySpan defilteredScanline, Span rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1) + private void ProcessInterlacedDefilteredScanline( + in FrameControl frameControl, + ReadOnlySpan defilteredScanline, + Span rowSpan, + PngMetadata pngMetadata, + int pixelOffset = 0, + int increment = 1) where TPixel : unmanaged, IPixel { // Trim the first marker byte from the buffer @@ -1034,6 +1047,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessInterlacedRgbaScanline( + this.configuration, this.header.BitDepth, in frameControl, scanlineSpan, diff --git a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs index a68d45ae0c..3325c6ba1a 100644 --- a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs @@ -53,7 +53,7 @@ public class PngFrameMetadata : IDeepCloneable /// Initializes a new instance of the class. /// /// The chunk to create an instance from. - internal void FromChunk(FrameControl frameControl) + internal void FromChunk(in FrameControl frameControl) { this.DelayNumerator = frameControl.DelayNumerator; this.DelayDenominator = frameControl.DelayDenominator; diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index 82faef3fe2..f217515e3c 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -216,18 +216,18 @@ internal static class PngScanlineProcessor int bytesPerPixel, int bytesPerSample, Color? transparentColor) - where TPixel : unmanaged, IPixel => - ProcessInterlacedRgbScanline( - configuration, - bitDepth, - frameControl, - scanlineSpan, - rowSpan, - 0, - 1, - bytesPerPixel, - bytesPerSample, - transparentColor); + where TPixel : unmanaged, IPixel => + ProcessInterlacedRgbScanline( + configuration, + bitDepth, + frameControl, + scanlineSpan, + rowSpan, + 0, + 1, + bytesPerPixel, + bytesPerSample, + transparentColor); public static void ProcessInterlacedRgbScanline( Configuration configuration, @@ -264,9 +264,16 @@ internal static class PngScanlineProcessor Unsafe.Add(ref rowSpanRef, x) = pixel; } } + else if (pixelOffset == 0 && increment == 1) + { + PixelOperations.Instance.FromRgb24Bytes( + configuration, + scanlineSpan[..(int)(frameControl.Width * bytesPerPixel)], + rowSpan.Slice((int)frameControl.XOffset, (int)frameControl.Width), + (int)frameControl.Width); + } else { - // TODO: Investigate reintroducing bulk operations optimization here. Rgb24 rgb = default; int o = 0; for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) @@ -323,24 +330,27 @@ internal static class PngScanlineProcessor } public static void ProcessRgbaScanline( + Configuration configuration, int bitDepth, in FrameControl frameControl, ReadOnlySpan scanlineSpan, Span rowSpan, int bytesPerPixel, int bytesPerSample) - where TPixel : unmanaged, IPixel => - ProcessInterlacedRgbaScanline( - bitDepth, - frameControl, - scanlineSpan, - rowSpan, - 0, - 1, - bytesPerPixel, - bytesPerSample); + where TPixel : unmanaged, IPixel => + ProcessInterlacedRgbaScanline( + configuration, + bitDepth, + frameControl, + scanlineSpan, + rowSpan, + 0, + 1, + bytesPerPixel, + bytesPerSample); public static void ProcessInterlacedRgbaScanline( + Configuration configuration, int bitDepth, in FrameControl frameControl, ReadOnlySpan scanlineSpan, @@ -370,9 +380,16 @@ internal static class PngScanlineProcessor Unsafe.Add(ref rowSpanRef, x) = pixel; } } + else if (pixelOffset == 0 && increment == 1) + { + PixelOperations.Instance.FromRgba32Bytes( + configuration, + scanlineSpan[..(int)(frameControl.Width * bytesPerPixel)], + rowSpan.Slice((int)frameControl.XOffset, (int)frameControl.Width), + (int)frameControl.Width); + } else { - // TODO: Investigate reintroducing bulk operations optimization here. ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); Rgba32 rgba = default; int o = 0;