From 13e7cf358fb64b18aa06ba646f0d6feedac426fc Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sun, 30 May 2021 15:43:48 +0300 Subject: [PATCH] Divided YCbCr converters into 444/420 subsampling categories --- .../Components/Encoder/HuffmanScanEncoder.cs | 4 +- .../YCbCrForwardConverter444{TPixel}.cs | 118 +++++++++++++++++ .../Encoder/YCbCrForwardConverter{TPixel}.cs | 122 ++---------------- 3 files changed, 130 insertions(+), 114 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index f6e55153a..4fbd9e4ec 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; - var pixelConverter = YCbCrForwardConverter.Create(); + var pixelConverter = YCbCrForwardConverter444.Create(); ImageFrame frame = pixels.Frames.RootFrame; Buffer2D pixelBuffer = frame.PixelBuffer; RowOctet currentRows = default; @@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { var unzig = ZigZag.CreateUnzigTable(); - var pixelConverter = YCbCrForwardConverter.Create(); + var pixelConverter = YCbCrForwardConverter444.Create(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs new file mode 100644 index 000000000..58bb1d559 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs @@ -0,0 +1,118 @@ +// Copyright (c) Six Labors. +// 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; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + /// + /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks. + /// + /// The pixel type to work on + internal ref struct YCbCrForwardConverter444 + where TPixel : unmanaged, IPixel + { + /// + /// The Y component + /// + public Block8x8F Y; + + /// + /// The Cb component + /// + public Block8x8F Cb; + + /// + /// The Cr component + /// + public Block8x8F Cr; + + /// + /// The color conversion tables + /// + private RgbToYCbCrConverterLut colorTables; + + /// + /// Temporal 8x8 block to hold TPixel data + /// + private Span pixelSpan; + + /// + /// Temporal RGB block + /// + private Span rgbSpan; + + public Span twinBlocksY; + + public static YCbCrForwardConverter444 Create() + { + var result = default(YCbCrForwardConverter444); + + // creating rgb pixel bufferr + // TODO: this is subject to discuss + const int twoBlocksByteSizeWithPadding = 384 + 8; // converter.Convert comments for +8 padding + result.rgbSpan = MemoryMarshal.Cast(new byte[twoBlocksByteSizeWithPadding].AsSpan()); + // TODO: this size should be configurable + result.pixelSpan = new TPixel[128].AsSpan(); + + result.twinBlocksY = new Block8x8F[2].AsSpan(); + + // Avoid creating lookup tables, when vectorized converter is supported + if (!RgbToYCbCrConverterVectorized.IsSupported) + { + result.colorTables = RgbToYCbCrConverterLut.Create(); + } + + return result; + } + + /// + /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) + /// + public void Convert(ImageFrame frame, int x, int y, ref RowOctet currentRows) + { + Memory.Buffer2D buffer = frame.PixelBuffer; + YCbCrForwardConverter.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), new Size(8), new Size(buffer.Width, buffer.Height)); + + PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan); + + ref Block8x8F yBlock = ref this.Y; + ref Block8x8F cbBlock = ref this.Cb; + ref Block8x8F crBlock = ref this.Cr; + + if (RgbToYCbCrConverterVectorized.IsSupported) + { + RgbToYCbCrConverterVectorized.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); + } + else + { + this.colorTables.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); + } + } + + /// + /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) + /// + public void Convert420(ImageFrame frame, int x, int y, ref RowOctet currentRows, int idx) + { + Memory.Buffer2D buffer = frame.PixelBuffer; + YCbCrForwardConverter.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), new Size(16, 8), new Size(buffer.Width, buffer.Height)); + + PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan); + + if (RgbToYCbCrConverterVectorized.IsSupported) + { + RgbToYCbCrConverterVectorized.Convert420_16x8(this.rgbSpan, this.twinBlocksY, ref this.Cb, ref this.Cr, idx); + } + else + { + throw new NotSupportedException("This is not yet implemented"); + //this.colorTables.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index 963e6dd9e..f5ef77091 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -4,134 +4,32 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { - /// - /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks. - /// - /// The pixel type to work on - internal ref struct YCbCrForwardConverter + internal static class YCbCrForwardConverter where TPixel : unmanaged, IPixel { - /// - /// The Y component - /// - public Block8x8F Y; - - /// - /// The Cb component - /// - public Block8x8F Cb; - - /// - /// The Cr component - /// - public Block8x8F Cr; - - /// - /// The color conversion tables - /// - private RgbToYCbCrConverterLut colorTables; - - /// - /// Temporal 8x8 block to hold TPixel data - /// - private Span pixelSpan; - - /// - /// Temporal RGB block - /// - private Span rgbSpan; - - public Span twinBlocksY; - - public static YCbCrForwardConverter Create() - { - var result = default(YCbCrForwardConverter); - - // creating rgb pixel bufferr - // TODO: this is subject to discuss - const int twoBlocksByteSizeWithPadding = 384 + 8; // converter.Convert comments for +8 padding - result.rgbSpan = MemoryMarshal.Cast(new byte[twoBlocksByteSizeWithPadding].AsSpan()); - // TODO: this size should be configurable - result.pixelSpan = new TPixel[128].AsSpan(); - - result.twinBlocksY = new Block8x8F[2].AsSpan(); - - // Avoid creating lookup tables, when vectorized converter is supported - if (!RgbToYCbCrConverterVectorized.IsSupported) - { - result.colorTables = RgbToYCbCrConverterLut.Create(); - } - - return result; - } - - /// - /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) - /// - public void Convert(ImageFrame frame, int x, int y, ref RowOctet currentRows) + public static void LoadAndStretchEdges(RowOctet source, Span dest, Point start, Size sampleSize, Size totalSize) { - Memory.Buffer2D buffer = frame.PixelBuffer; - LoadAndStretchEdges(currentRows, this.pixelSpan, x, y, new Size(8), new Size(buffer.Width, buffer.Height)); - - PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan); - - ref Block8x8F yBlock = ref this.Y; - ref Block8x8F cbBlock = ref this.Cb; - ref Block8x8F crBlock = ref this.Cr; + DebugGuard.MustBeBetweenOrEqualTo(start.X, 1, totalSize.Width - 1, nameof(start.X)); + DebugGuard.MustBeBetweenOrEqualTo(start.Y, 1, totalSize.Height - 1, nameof(start.Y)); - if (RgbToYCbCrConverterVectorized.IsSupported) - { - RgbToYCbCrConverterVectorized.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); - } - else - { - this.colorTables.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); - } - } - - /// - /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) - /// - public void Convert420(ImageFrame frame, int x, int y, ref RowOctet currentRows, int idx) - { - Memory.Buffer2D buffer = frame.PixelBuffer; - LoadAndStretchEdges(currentRows, this.pixelSpan, x, y, new Size(16, 8), new Size(buffer.Width, buffer.Height)); - - PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan); - - if (RgbToYCbCrConverterVectorized.IsSupported) - { - RgbToYCbCrConverterVectorized.Convert420_16x8(this.rgbSpan, this.twinBlocksY, ref this.Cb, ref this.Cr, idx); - } - else - { - throw new NotSupportedException("This is not yet implemented"); - //this.colorTables.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); - } - } - - - private static void LoadAndStretchEdges(RowOctet source, Span dest, int startX, int startY, Size areaSize, Size borders) - { - int width = Math.Min(areaSize.Width, borders.Width - startX); - int height = Math.Min(areaSize.Height, borders.Height - startY); + int width = Math.Min(sampleSize.Width, totalSize.Width - start.X); + int height = Math.Min(sampleSize.Height, totalSize.Height - start.Y); uint byteWidth = (uint)(width * Unsafe.SizeOf()); - int remainderXCount = areaSize.Width - width; + int remainderXCount = sampleSize.Width - width; ref byte blockStart = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(dest)); - int rowSizeInBytes = areaSize.Width * Unsafe.SizeOf(); + int rowSizeInBytes = sampleSize.Width * Unsafe.SizeOf(); for (int y = 0; y < height; y++) { Span row = source[y]; - ref byte s = ref Unsafe.As(ref row[startX]); + ref byte s = ref Unsafe.As(ref row[start.X]); ref byte d = ref Unsafe.Add(ref blockStart, y * rowSizeInBytes); Unsafe.CopyBlock(ref d, ref s, byteWidth); @@ -144,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } } - int remainderYCount = areaSize.Height - height; + int remainderYCount = sampleSize.Height - height; if (remainderYCount == 0) {