From 052ebde3ad4abd3a68d9648a66fc4ae9be37df82 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sat, 29 May 2021 22:31:17 +0300 Subject: [PATCH] Replaced GenericBlocl8x8 with Span in ycbcr converter --- .../Encoder/YCbCrForwardConverter{TPixel}.cs | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index c835e8df81..952dde1112 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// /// Temporal 8x8 block to hold TPixel data /// - private GenericBlock8x8 pixelBlock; + private Span pixelSpan; /// /// Temporal RGB block @@ -52,6 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // creating rgb pixel bufferr // TODO: this is subject to discuss result.rgbSpan = MemoryMarshal.Cast(new byte[200].AsSpan()); + result.pixelSpan = new TPixel[64].AsSpan(); // Avoid creating lookup tables, when vectorized converter is supported if (!RgbToYCbCrConverterVectorized.IsSupported) @@ -67,9 +69,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// public void Convert(ImageFrame frame, int x, int y, ref RowOctet currentRows) { - this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows); + Memory.Buffer2D buffer = frame.PixelBuffer; + LoadAndStretchEdges(currentRows, this.pixelSpan, x, buffer.Width - x, buffer.Height - y); - PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), this.rgbSpan); + PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan); ref Block8x8F yBlock = ref this.Y; ref Block8x8F cbBlock = ref this.Cb; @@ -90,9 +93,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// public void Convert420(ImageFrame frame, int x, int y, ref RowOctet currentRows, int idx) { - this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows); + Memory.Buffer2D buffer = frame.PixelBuffer; + LoadAndStretchEdges(currentRows, this.pixelSpan, x, Math.Min(8, buffer.Width - x), Math.Min(8, buffer.Height - y)); - PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), this.rgbSpan); + PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan); if (RgbToYCbCrConverterVectorized.IsSupported) { @@ -104,5 +108,56 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder //this.colorTables.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); } } + + // TODO: add DebugGuard checks? + private static void LoadAndStretchEdges(RowOctet source, Span dest, int startX, int width, int height) + { + //Guard.MustBeBetweenOrEqualTo(width, 1, 8, nameof(width)); + //Guard.MustBeBetweenOrEqualTo(height, 1, 8, nameof(width)); + + // TODO: this is a strange check, most likely it was introduces due to 2x 8x8 blocks subsampling, should be gone after new 4:2:0 implementation + if (width <= 0 || height <= 0) + { + return; + } + + uint byteWidth = (uint)(width * Unsafe.SizeOf()); + int remainderXCount = 8 - width; + + ref byte blockStart = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(dest)); + int rowSizeInBytes = 8 * Unsafe.SizeOf(); + + for (int y = 0; y < height; y++) + { + Span row = source[y]; + + ref byte s = ref Unsafe.As(ref row[startX]); + ref byte d = ref Unsafe.Add(ref blockStart, y * rowSizeInBytes); + + Unsafe.CopyBlock(ref d, ref s, byteWidth); + + ref TPixel last = ref Unsafe.Add(ref Unsafe.As(ref d), width - 1); + + for (int x = 1; x <= remainderXCount; x++) + { + Unsafe.Add(ref last, x) = last; + } + } + + int remainderYCount = 8 - height; + + if (remainderYCount == 0) + { + return; + } + + ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * rowSizeInBytes); + + for (int y = 1; y <= remainderYCount; y++) + { + ref byte remStart = ref Unsafe.Add(ref lastRowStart, rowSizeInBytes * y); + Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)rowSizeInBytes); + } + } } }