From 7a2c22a966cee0f54f19f7122bc762e80cd0f6b3 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Thu, 25 Nov 2021 20:06:44 +0300 Subject: [PATCH 01/15] Removed obsolete/redundant/unused code from Block8x8F --- .../Formats/Jpeg/Components/Block8x8F.cs | 116 ------------------ .../Formats/Jpg/Block8x8FTests.cs | 50 -------- ...plementationsTests.FastFloatingPointDCT.cs | 2 +- ...ImplementationsTests.StandardIntegerDCT.cs | 4 +- 4 files changed, 3 insertions(+), 169 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 02f5a1324..f85e57d29 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -98,58 +97,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components set => this[(y * 8) + x] = value; } - public static Block8x8F operator *(Block8x8F block, float value) - { - Block8x8F result = block; - for (int i = 0; i < Size; i++) - { - float val = result[i]; - val *= value; - result[i] = val; - } - - return result; - } - - public static Block8x8F operator /(Block8x8F block, float value) - { - Block8x8F result = block; - for (int i = 0; i < Size; i++) - { - float val = result[i]; - val /= value; - result[i] = val; - } - - return result; - } - - public static Block8x8F operator +(Block8x8F block, float value) - { - Block8x8F result = block; - for (int i = 0; i < Size; i++) - { - float val = result[i]; - val += value; - result[i] = val; - } - - return result; - } - - public static Block8x8F operator -(Block8x8F block, float value) - { - Block8x8F result = block; - for (int i = 0; i < Size; i++) - { - float val = result[i]; - val -= value; - result[i] = val; - } - - return result; - } - public static Block8x8F Load(Span data) { Block8x8F result = default; @@ -177,15 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); } - /// - /// Load raw 32bit floating point data from source. - /// - /// Block pointer - /// Source - [MethodImpl(InliningOptions.ShortMethod)] - public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source) - => blockPtr->LoadFrom(source); - /// /// Load raw 32bit floating point data from source /// @@ -202,44 +140,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - /// - /// Copy raw 32bit floating point data to dest, - /// - /// Destination - [MethodImpl(InliningOptions.ShortMethod)] - public void ScaledCopyTo(Span dest) - { - ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - ref byte s = ref Unsafe.As(ref this); - - Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); - } - - /// - /// Convert scalars to byte-s and copy to dest, - /// - /// Pointer to block - /// Destination - [MethodImpl(InliningOptions.ShortMethod)] - public static unsafe void ScaledCopyTo(Block8x8F* blockPtr, Span dest) - { - float* fPtr = (float*)blockPtr; - for (int i = 0; i < Size; i++) - { - dest[i] = (byte)*fPtr; - fPtr++; - } - } - - /// - /// Copy raw 32bit floating point data to dest. - /// - /// The block pointer. - /// The destination. - [MethodImpl(InliningOptions.ShortMethod)] - public static unsafe void ScaledCopyTo(Block8x8F* blockPtr, Span dest) - => blockPtr->ScaledCopyTo(dest); - /// /// Copy raw 32bit floating point data to dest /// @@ -253,22 +153,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - /// - /// Copy raw 32bit floating point data to dest - /// - /// Destination - public unsafe void ScaledCopyTo(Span dest) - { - fixed (Vector4* ptr = &this.V0L) - { - var fp = (float*)ptr; - for (int i = 0; i < Size; i++) - { - dest[i] = (int)fp[i]; - } - } - } - public float[] ToArray() { float[] result = new float[Size]; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 8f5f10f19..ae7e81254 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -114,56 +114,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // PrintLinearData((Span)mirror); } - [Fact] - public unsafe void Load_Store_FloatArray_Ptr() - { - float[] data = new float[Block8x8F.Size]; - float[] mirror = new float[Block8x8F.Size]; - - for (int i = 0; i < Block8x8F.Size; i++) - { - data[i] = i; - } - - this.Measure( - Times, - () => - { - var b = default(Block8x8F); - Block8x8F.LoadFrom(&b, data); - Block8x8F.ScaledCopyTo(&b, mirror); - }); - - Assert.Equal(data, mirror); - - // PrintLinearData((Span)mirror); - } - - [Fact] - public void Load_Store_IntArray() - { - int[] data = new int[Block8x8F.Size]; - int[] mirror = new int[Block8x8F.Size]; - - for (int i = 0; i < Block8x8F.Size; i++) - { - data[i] = i; - } - - this.Measure( - Times, - () => - { - var v = default(Block8x8F); - v.LoadFrom(data); - v.ScaledCopyTo(mirror); - }); - - Assert.Equal(data, mirror); - - // PrintLinearData((Span)mirror); - } - [Fact] public void TransposeInplace() { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs index 8920d42d4..9eb3c0103 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformFDCT_UpscaleBy8(ref source); - actual /= 8; + actual.MultiplyInPlace(0.125f); this.CompareBlocks(expected, actual, 1f); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs index 6e25334f2..02f8ba388 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs @@ -51,11 +51,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); - source += 128; + source.AddInPlace(128f); Block8x8 temp = source.RoundAsInt16Block(); Block8x8 actual8 = ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8(ref temp); Block8x8F actual = actual8.AsFloatBlock(); - actual /= 8; + actual.MultiplyInPlace(0.125f); this.CompareBlocks(expected, actual, 1f); } From 60fd3b0659c6ba464d59eefe0cb09d084397fe0d Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Fri, 26 Nov 2021 06:51:42 +0300 Subject: [PATCH 02/15] Removed GenericBlock8x8 --- .../Components/Encoder/HuffmanScanEncoder.cs | 5 +- .../LuminanceForwardConverter{TPixel}.cs | 49 +++++-- .../Components/GenericBlock8x8.Generated.cs | 23 ---- .../Components/GenericBlock8x8.Generated.tt | 0 .../Jpeg/Components/GenericBlock8x8.cs | 122 ----------------- .../Formats/Jpg/GenericBlock8x8Tests.cs | 129 ------------------ 6 files changed, 40 insertions(+), 288 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.tt delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index b3cdbf0a0..d71a62ec9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -278,11 +278,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // ReSharper disable once InconsistentNaming int prevDCY = 0; - var pixelConverter = LuminanceForwardConverter.Create(); ImageFrame frame = pixels.Frames.RootFrame; Buffer2D pixelBuffer = frame.PixelBuffer; RowOctet currentRows = default; + var pixelConverter = new LuminanceForwardConverter(frame); + for (int y = 0; y < pixels.Height; y += 8) { cancellationToken.ThrowIfCancellationRequested(); @@ -290,7 +291,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder for (int x = 0; x < pixels.Width; x += 8) { - pixelConverter.Convert(frame, x, y, ref currentRows); + pixelConverter.Convert(x, y, ref currentRows); prevDCY = this.WriteBlock( QuantIndex.Luminance, diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs index fc5b9a868..b6edfefed 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -15,39 +16,63 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder internal ref struct LuminanceForwardConverter where TPixel : unmanaged, IPixel { + /// + /// Number of pixels processed per single call + /// + private const int PixelsPerSample = 8 * 8; + + /// + /// of sampling area from given frame pixel buffer. + /// + private static readonly Size SampleSize = new Size(8, 8); + /// /// The Y component /// public Block8x8F Y; /// - /// Temporal 8x8 block to hold TPixel data + /// Temporal 64-pixel span to hold unconverted TPixel data. + /// + private readonly Span pixelSpan; + + /// + /// Temporal 64-byte span to hold converted data. /// - private GenericBlock8x8 pixelBlock; + private readonly Span l8Span; /// - /// Temporal RGB block + /// Sampled pixel buffer size. /// - private GenericBlock8x8 l8Block; + private readonly Size samplingAreaSize; - public static LuminanceForwardConverter Create() + /// + /// for internal operations. + /// + private readonly Configuration config; + + public LuminanceForwardConverter(ImageFrame frame) { - var result = default(LuminanceForwardConverter); - return result; + this.Y = default; + + this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); + this.l8Span = new L8[PixelsPerSample].AsSpan(); + + this.samplingAreaSize = new Size(frame.Width, frame.Height); + this.config = frame.GetConfiguration(); } /// /// 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 void Convert(int x, int y, ref RowOctet currentRows) { - this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows); + YCbCrForwardConverter.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); - Span l8Span = this.l8Block.AsSpanUnsafe(); - PixelOperations.Instance.ToL8(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), l8Span); + PixelOperations.Instance.ToL8(this.config, this.pixelSpan, this.l8Span); ref Block8x8F yBlock = ref this.Y; - ref L8 l8Start = ref l8Span[0]; + ref L8 l8Start = ref MemoryMarshal.GetReference(this.l8Span); for (int i = 0; i < Block8x8F.Size; i++) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs deleted file mode 100644 index 213c48ff3..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -// -namespace SixLabors.ImageSharp.Formats.Jpeg.Components -{ - internal unsafe partial struct GenericBlock8x8 - { - #pragma warning disable 169 - - // It's not allowed use fix-sized buffers with generics, need to place all the fields manually: - private T _y0_x0, _y0_x1, _y0_x2, _y0_x3, _y0_x4, _y0_x5, _y0_x6, _y0_x7; - private T _y1_x0, _y1_x1, _y1_x2, _y1_x3, _y1_x4, _y1_x5, _y1_x6, _y1_x7; - private T _y2_x0, _y2_x1, _y2_x2, _y2_x3, _y2_x4, _y2_x5, _y2_x6, _y2_x7; - private T _y3_x0, _y3_x1, _y3_x2, _y3_x3, _y3_x4, _y3_x5, _y3_x6, _y3_x7; - private T _y4_x0, _y4_x1, _y4_x2, _y4_x3, _y4_x4, _y4_x5, _y4_x6, _y4_x7; - private T _y5_x0, _y5_x1, _y5_x2, _y5_x3, _y5_x4, _y5_x5, _y5_x6, _y5_x7; - private T _y6_x0, _y6_x1, _y6_x2, _y6_x3, _y6_x4, _y6_x5, _y6_x6, _y6_x7; - private T _y7_x0, _y7_x1, _y7_x2, _y7_x3, _y7_x4, _y7_x5, _y7_x6, _y7_x7; - - #pragma warning restore 169 - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.tt deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs deleted file mode 100644 index 42c01d770..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ /dev/null @@ -1,122 +0,0 @@ -// 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.Memory; -using SixLabors.ImageSharp.PixelFormats; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Formats.Jpeg.Components -{ - /// - /// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data. - /// - [StructLayout(LayoutKind.Sequential)] - internal unsafe partial struct GenericBlock8x8 - where T : unmanaged - { - public const int Size = 64; - - /// - /// FOR TESTING ONLY! - /// Gets or sets a value at the given index - /// - /// The index - /// The value - public T this[int idx] - { - get - { - ref T selfRef = ref Unsafe.As, T>(ref this); - return Unsafe.Add(ref selfRef, idx); - } - - set - { - ref T selfRef = ref Unsafe.As, T>(ref this); - Unsafe.Add(ref selfRef, idx) = value; - } - } - - /// - /// FOR TESTING ONLY! - /// Gets or sets a value in a row+column of the 8x8 block - /// - /// The x position index in the row - /// The column index - /// The value - public T this[int x, int y] - { - get => this[(y * 8) + x]; - set => this[(y * 8) + x] = value; - } - - /// - /// Load a 8x8 region of an image into the block. - /// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image. - /// - public void LoadAndStretchEdges(Buffer2D source, int sourceX, int sourceY, ref RowOctet currentRows) - { - int width = Math.Min(8, source.Width - sourceX); - int height = Math.Min(8, source.Height - sourceY); - - if (width <= 0 || height <= 0) - { - return; - } - - uint byteWidth = (uint)width * (uint)Unsafe.SizeOf(); - int remainderXCount = 8 - width; - - ref byte blockStart = ref Unsafe.As, byte>(ref this); - int blockRowSizeInBytes = 8 * Unsafe.SizeOf(); - - for (int y = 0; y < height; y++) - { - Span row = currentRows[y]; - - ref byte s = ref Unsafe.As(ref row[sourceX]); - ref byte d = ref Unsafe.Add(ref blockStart, y * blockRowSizeInBytes); - - Unsafe.CopyBlock(ref d, ref s, byteWidth); - - ref T 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) * blockRowSizeInBytes); - - for (int y = 1; y <= remainderYCount; y++) - { - ref byte remStart = ref Unsafe.Add(ref lastRowStart, blockRowSizeInBytes * y); - Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)blockRowSizeInBytes); - } - } - - /// - /// Only for on-stack instances! - /// - public Span AsSpanUnsafe() - { -#if SUPPORTS_CREATESPAN - Span> s = MemoryMarshal.CreateSpan(ref this, 1); - return MemoryMarshal.Cast, T>(s); -#else - return new Span(Unsafe.AsPointer(ref this), Size); -#endif - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs deleted file mode 100644 index d5ee2a2b8..000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Formats.Jpg -{ - [Trait("Format", "Jpg")] - public class GenericBlock8x8Tests - { - public static Image CreateTestImage() - where TPixel : unmanaged, IPixel - { - var image = new Image(10, 10); - Buffer2D pixels = image.GetRootFramePixelBuffer(); - for (int i = 0; i < 10; i++) - { - for (int j = 0; j < 10; j++) - { - var rgba = new Rgba32((byte)(i + 1), (byte)(j + 1), 200, 255); - var color = default(TPixel); - color.FromRgba32(rgba); - - pixels[i, j] = color; - } - } - - return image; - } - - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgb24 | PixelTypes.Rgba32 /* | PixelTypes.Rgba32 | PixelTypes.Argb32*/)] - public void LoadAndStretchCorners_FromOrigo(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using (Image s = provider.GetImage()) - { - var d = default(GenericBlock8x8); - RowOctet rowOctet = default; - rowOctet.Update(s.GetRootFramePixelBuffer(), 0); - d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 0, 0, ref rowOctet); - - TPixel a = s.Frames.RootFrame[0, 0]; - TPixel b = d[0, 0]; - - Assert.Equal(s[0, 0], d[0, 0]); - Assert.Equal(s[1, 0], d[1, 0]); - Assert.Equal(s[7, 0], d[7, 0]); - Assert.Equal(s[0, 1], d[0, 1]); - Assert.Equal(s[1, 1], d[1, 1]); - Assert.Equal(s[7, 0], d[7, 0]); - Assert.Equal(s[0, 7], d[0, 7]); - Assert.Equal(s[7, 7], d[7, 7]); - } - } - - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgb24 | PixelTypes.Rgba32)] - public void LoadAndStretchCorners_WithOffset(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using (Image s = provider.GetImage()) - { - var d = default(GenericBlock8x8); - RowOctet rowOctet = default; - rowOctet.Update(s.GetRootFramePixelBuffer(), 7); - - d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 6, 7, ref rowOctet); - - Assert.Equal(s[6, 7], d[0, 0]); - Assert.Equal(s[6, 8], d[0, 1]); - Assert.Equal(s[7, 8], d[1, 1]); - - Assert.Equal(s[6, 9], d[0, 2]); - Assert.Equal(s[6, 9], d[0, 3]); - Assert.Equal(s[6, 9], d[0, 7]); - - Assert.Equal(s[7, 9], d[1, 2]); - Assert.Equal(s[7, 9], d[1, 3]); - Assert.Equal(s[7, 9], d[1, 7]); - - Assert.Equal(s[9, 9], d[3, 2]); - Assert.Equal(s[9, 9], d[3, 3]); - Assert.Equal(s[9, 9], d[3, 7]); - - Assert.Equal(s[9, 7], d[3, 0]); - Assert.Equal(s[9, 7], d[4, 0]); - Assert.Equal(s[9, 7], d[7, 0]); - - Assert.Equal(s[9, 9], d[3, 2]); - Assert.Equal(s[9, 9], d[4, 2]); - Assert.Equal(s[9, 9], d[7, 2]); - - Assert.Equal(s[9, 9], d[4, 3]); - Assert.Equal(s[9, 9], d[7, 7]); - } - } - - [Fact] - public void Indexer() - { - var block = default(GenericBlock8x8); - Span span = block.AsSpanUnsafe(); - Assert.Equal(64, span.Length); - - for (int i = 0; i < 64; i++) - { - span[i] = new Rgb24((byte)i, (byte)(2 * i), (byte)(3 * i)); - } - - var expected00 = new Rgb24(0, 0, 0); - var expected07 = new Rgb24(7, 14, 21); - var expected11 = new Rgb24(9, 18, 27); - var expected77 = new Rgb24(63, 126, 189); - var expected67 = new Rgb24(62, 124, 186); - - Assert.Equal(expected00, block[0, 0]); - Assert.Equal(expected07, block[7, 0]); - Assert.Equal(expected11, block[1, 1]); - Assert.Equal(expected67, block[6, 7]); - Assert.Equal(expected77, block[7, 7]); - } - } -} From 525cfc89aee92a5d2b939188b0141bae4d1b9223 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Fri, 26 Nov 2021 07:13:23 +0300 Subject: [PATCH 03/15] Removed redundant/unused code from Block8x8 --- .../Formats/Jpeg/Components/Block8x8.cs | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index 27bb2fc3c..5d669ff8a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -102,74 +102,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components set => this[(y * 8) + x] = value; } - public static bool operator ==(Block8x8 left, Block8x8 right) => left.Equals(right); - - public static bool operator !=(Block8x8 left, Block8x8 right) => !left.Equals(right); - - /// - /// Multiply all elements by a given - /// - public static Block8x8 operator *(Block8x8 block, int value) - { - Block8x8 result = block; - for (int i = 0; i < Size; i++) - { - int val = result[i]; - val *= value; - result[i] = (short)val; - } - - return result; - } - - /// - /// Divide all elements by a given - /// - public static Block8x8 operator /(Block8x8 block, int value) - { - Block8x8 result = block; - for (int i = 0; i < Size; i++) - { - int val = result[i]; - val /= value; - result[i] = (short)val; - } - - return result; - } - - /// - /// Add an to all elements - /// - public static Block8x8 operator +(Block8x8 block, int value) - { - Block8x8 result = block; - for (int i = 0; i < Size; i++) - { - int val = result[i]; - val += value; - result[i] = (short)val; - } - - return result; - } - - /// - /// Subtract an from all elements - /// - public static Block8x8 operator -(Block8x8 block, int value) - { - Block8x8 result = block; - for (int i = 0; i < Size; i++) - { - int val = result[i]; - val -= value; - result[i] = (short)val; - } - - return result; - } - public static Block8x8 Load(Span data) { Unsafe.SkipInit(out Block8x8 result); From 9c3d348b4d5fad512c4aeb03aa8c3558c191eb25 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Fri, 26 Nov 2021 08:15:19 +0300 Subject: [PATCH 04/15] Separated Block8x8 into scalar and intrinsic code bases --- .../Jpeg/Components/Block8x8.Intrinsic.cs | 43 +++++++++++++++++++ .../Formats/Jpeg/Components/Block8x8.cs | 30 +------------ src/ImageSharp/ImageSharp.csproj | 9 ---- 3 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs new file mode 100644 index 000000000..8265ad589 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +#if SUPPORTS_RUNTIME_INTRINSICS +using System; +using System.Runtime.InteropServices; +#if SUPPORTS_RUNTIME_INTRINSICS +using System.Runtime.Intrinsics; +#endif + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components +{ + + internal unsafe partial struct Block8x8 : IEquatable + { + [FieldOffset(0)] + public Vector128 V0; + [FieldOffset(16)] + public Vector128 V1; + [FieldOffset(32)] + public Vector128 V2; + [FieldOffset(48)] + public Vector128 V3; + [FieldOffset(64)] + public Vector128 V4; + [FieldOffset(80)] + public Vector128 V5; + [FieldOffset(96)] + public Vector128 V6; + [FieldOffset(112)] + public Vector128 V7; + + [FieldOffset(0)] + public Vector256 V01; + [FieldOffset(32)] + public Vector256 V23; + [FieldOffset(64)] + public Vector256 V45; + [FieldOffset(96)] + public Vector256 V67; + } +} +#endif diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index 5d669ff8a..e0d300eb5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// // ReSharper disable once InconsistentNaming [StructLayout(LayoutKind.Explicit)] - internal unsafe struct Block8x8 : IEquatable + internal unsafe partial struct Block8x8 : IEquatable { /// /// A number of scalar coefficients in a @@ -36,34 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components private fixed short data[Size]; #pragma warning restore IDE0051 -#if SUPPORTS_RUNTIME_INTRINSICS - [FieldOffset(0)] - public Vector128 V0; - [FieldOffset(16)] - public Vector128 V1; - [FieldOffset(32)] - public Vector128 V2; - [FieldOffset(48)] - public Vector128 V3; - [FieldOffset(64)] - public Vector128 V4; - [FieldOffset(80)] - public Vector128 V5; - [FieldOffset(96)] - public Vector128 V6; - [FieldOffset(112)] - public Vector128 V7; - - [FieldOffset(0)] - public Vector256 V01; - [FieldOffset(32)] - public Vector256 V23; - [FieldOffset(64)] - public Vector256 V45; - [FieldOffset(96)] - public Vector256 V67; -#endif - /// /// Gets or sets a value at the given index /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 6ad20713d..f90e40edb 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -70,11 +70,6 @@ True Block8x8F.Generated.tt - - True - True - GenericBlock8x8.Generated.tt - True True @@ -167,10 +162,6 @@ TextTemplatingFileGenerator Block8x8F.Generated.cs - - TextTemplatingFileGenerator - GenericBlock8x8.Generated.cs - TextTemplatingFileGenerator Block8x8F.Generated.cs From 140b07854e48afc838fe74d7ebac292d043a26eb Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Fri, 26 Nov 2021 08:50:55 +0300 Subject: [PATCH 05/15] Removed redundant code from Block8x8F and Block8x8 --- .../Jpeg/Components/Block8x8.Intrinsic.cs | 6 +---- .../Formats/Jpeg/Components/Block8x8.cs | 22 +------------------ .../Formats/Jpeg/Components/Block8x8F.cs | 7 ------ .../Formats/Jpg/Block8x8Tests.cs | 14 ------------ 4 files changed, 2 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs index 8265ad589..002d382dc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs @@ -2,16 +2,12 @@ // Licensed under the Apache License, Version 2.0. #if SUPPORTS_RUNTIME_INTRINSICS -using System; using System.Runtime.InteropServices; -#if SUPPORTS_RUNTIME_INTRINSICS using System.Runtime.Intrinsics; -#endif namespace SixLabors.ImageSharp.Formats.Jpeg.Components { - - internal unsafe partial struct Block8x8 : IEquatable + internal unsafe partial struct Block8x8 { [FieldOffset(0)] public Vector128 V0; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index e0d300eb5..4b03f9f7b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// // ReSharper disable once InconsistentNaming [StructLayout(LayoutKind.Explicit)] - internal unsafe partial struct Block8x8 : IEquatable + internal unsafe partial struct Block8x8 { /// /// A number of scalar coefficients in a @@ -164,26 +164,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components return sb.ToString(); } - /// - public bool Equals(Block8x8 other) - { - for (int i = 0; i < Size; i++) - { - if (this[i] != other[i]) - { - return false; - } - } - - return true; - } - - /// - public override bool Equals(object obj) => obj is Block8x8 other && this.Equals(other); - - /// - public override int GetHashCode() => (this[0] * 31) + this[1]; - /// /// Returns index of the last non-zero element in given matrix. /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index f85e57d29..f25286447 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -104,13 +104,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components return result; } - public static Block8x8F Load(Span data) - { - Block8x8F result = default; - result.LoadFrom(data); - return result; - } - /// /// Load raw 32bit floating point data from source. /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs index b13a196cb..094622070 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs @@ -70,20 +70,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(data, result); } - [Fact] - public void Equality_WhenTrue() - { - short[] data = Create8x8ShortData(); - var block1 = Block8x8.Load(data); - var block2 = Block8x8.Load(data); - - block1[0] = 42; - block2[0] = 42; - - Assert.Equal(block1, block2); - Assert.Equal(block1.GetHashCode(), block2.GetHashCode()); - } - [Fact] public void Equality_WhenFalse() { From 503b379e89315e7f6eebeb89d23dddf2038f3ca8 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sat, 27 Nov 2021 08:34:48 +0300 Subject: [PATCH 06/15] Removed JpegBlockPostProcessor abstraction layer --- .../Decoder/JpegBlockPostProcessor.cs | 82 ------------------- .../Decoder/JpegComponentPostProcessor.cs | 28 ++++++- 2 files changed, 24 insertions(+), 86 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs deleted file mode 100644 index 15f212b40..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - /// - /// Encapsulates the implementation of processing "raw" jpeg buffers into Jpeg image channels. - /// - [StructLayout(LayoutKind.Sequential)] - internal struct JpegBlockPostProcessor - { - /// - /// Source block - /// - public Block8x8F SourceBlock; - - /// - /// The quantization table as . - /// - public Block8x8F DequantiazationTable; - - /// - /// Defines the horizontal and vertical scale we need to apply to the 8x8 sized block. - /// - private Size subSamplingDivisors; - - /// - /// Initializes a new instance of the struct. - /// - /// The raw jpeg data. - /// The raw component. - public JpegBlockPostProcessor(IRawJpegData decoder, IJpegComponent component) - { - int qtIndex = component.QuantizationTableIndex; - this.DequantiazationTable = decoder.QuantizationTables[qtIndex]; - this.subSamplingDivisors = component.SubSamplingDivisors; - - this.SourceBlock = default; - } - - /// - /// Processes 'sourceBlock' producing Jpeg color channel values from spectral values: - /// - Dequantize - /// - Applying IDCT - /// - Level shift by +maximumValue/2, clip to [0, maximumValue] - /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in . - /// - /// The source block. - /// Reference to the origin of the destination pixel area. - /// The width of the destination pixel buffer. - /// The maximum value derived from the bitdepth. - public void ProcessBlockColorsInto( - ref Block8x8 sourceBlock, - ref float destAreaOrigin, - int destAreaStride, - float maximumValue) - { - ref Block8x8F block = ref this.SourceBlock; - block.LoadFrom(ref sourceBlock); - - // Dequantize: - block.MultiplyInPlace(ref this.DequantiazationTable); - - FastFloatingPointDCT.TransformIDCT(ref block); - - // To conform better to libjpeg we actually NEED TO loose precision here. - // This is because they store blocks as Int16 between all the operations. - // To be "more accurate", we need to emulate this by rounding! - block.NormalizeColorsAndRoundInPlace(maximumValue); - - block.ScaledCopyTo( - ref destAreaOrigin, - destAreaStride, - this.subSamplingDivisors.Width, - this.subSamplingDivisors.Height); - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index 9a659d621..b26aae8d8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -76,14 +76,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { Buffer2D spectralBuffer = this.Component.SpectralBlocks; - var blockPp = new JpegBlockPostProcessor(this.RawJpeg, this.Component); - float maximumValue = this.frame.MaxColorChannelValue; int destAreaStride = this.ColorBuffer.Width; int yBlockStart = step * this.BlockRowsPerStep; + Size subSamplingDivisors = this.Component.SubSamplingDivisors; + + Block8x8F dequantTable = this.RawJpeg.QuantizationTables[this.Component.QuantizationTableIndex]; + Block8x8F workspaceBlock = default; + for (int y = 0; y < this.BlockRowsPerStep; y++) { int yBlock = yBlockStart + y; @@ -103,11 +106,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int xBlock = 0; xBlock < widthInBlocks; xBlock++) { - ref Block8x8 block = ref blockRow[xBlock]; int xBuffer = xBlock * this.blockAreaSize.Width; ref float destAreaOrigin = ref colorBufferRow[xBuffer]; - blockPp.ProcessBlockColorsInto(ref block, ref destAreaOrigin, destAreaStride, maximumValue); + workspaceBlock.LoadFrom(ref blockRow[xBlock]); + + // Dequantize + workspaceBlock.MultiplyInPlace(ref dequantTable); + + // Convert from spectral to color + FastFloatingPointDCT.TransformIDCT(ref workspaceBlock); + + // To conform better to libjpeg we actually NEED TO loose precision here. + // This is because they store blocks as Int16 between all the operations. + // To be "more accurate", we need to emulate this by rounding! + workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); + + // Write to color buffer acording to sampling factors + workspaceBlock.ScaledCopyTo( + ref destAreaOrigin, + destAreaStride, + subSamplingDivisors.Width, + subSamplingDivisors.Height); } } } From 0d3e7fff5339f7ceed1d9c74ebb6513c29c06d9b Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sat, 27 Nov 2021 09:07:29 +0300 Subject: [PATCH 07/15] Removed redundant if check, removed used code --- .../Decoder/JpegComponentPostProcessor.cs | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index b26aae8d8..e7b0f1b9a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -11,11 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// internal class JpegComponentPostProcessor : IDisposable { - /// - /// Points to the current row in . - /// - private int currentComponentRowInBlocks; - /// /// The size of the area in corresponding to one 8x8 Jpeg block /// @@ -70,9 +65,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public void Dispose() => this.ColorBuffer.Dispose(); /// - /// Invoke for block rows, copy the result into . + /// Convert raw spectral DCT data to color data and copy it to the color buffer . /// - public void CopyBlocksToColorBuffer(int step) + public void CopyBlocksToColorBuffer(int spectralStep) { Buffer2D spectralBuffer = this.Component.SpectralBlocks; @@ -80,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int destAreaStride = this.ColorBuffer.Width; - int yBlockStart = step * this.BlockRowsPerStep; + int yBlockStart = spectralStep * this.BlockRowsPerStep; Size subSamplingDivisors = this.Component.SubSamplingDivisors; @@ -89,26 +84,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int y = 0; y < this.BlockRowsPerStep; y++) { - int yBlock = yBlockStart + y; - - if (yBlock >= spectralBuffer.Height) - { - break; - } - int yBuffer = y * this.blockAreaSize.Height; Span colorBufferRow = this.ColorBuffer.GetRowSpan(yBuffer); - Span blockRow = spectralBuffer.GetRowSpan(yBlock); + Span blockRow = spectralBuffer.GetRowSpan(yBlockStart + y); // see: https://github.com/SixLabors/ImageSharp/issues/824 int widthInBlocks = Math.Min(spectralBuffer.Width, this.SizeInBlocks.Width); for (int xBlock = 0; xBlock < widthInBlocks; xBlock++) { - int xBuffer = xBlock * this.blockAreaSize.Width; - ref float destAreaOrigin = ref colorBufferRow[xBuffer]; - + // Integer to float workspaceBlock.LoadFrom(ref blockRow[xBlock]); // Dequantize @@ -123,8 +109,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); // Write to color buffer acording to sampling factors + int xColorBufferStart = xBlock * this.blockAreaSize.Width; workspaceBlock.ScaledCopyTo( - ref destAreaOrigin, + ref colorBufferRow[xColorBufferStart], destAreaStride, subSamplingDivisors.Width, subSamplingDivisors.Height); @@ -140,11 +127,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder spectralBlocks.GetRowSpan(i).Clear(); } } - - public void CopyBlocksToColorBuffer() - { - this.CopyBlocksToColorBuffer(this.currentComponentRowInBlocks); - this.currentComponentRowInBlocks += this.BlockRowsPerStep; - } } } From 97a0dccfc9aab9a4a5ebd07d0f5674efcd02336e Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sat, 27 Nov 2021 10:05:40 +0300 Subject: [PATCH 08/15] Removed redundant if check (min call) --- .../Jpeg/Components/Decoder/JpegComponentPostProcessor.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index e7b0f1b9a..35f421263 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -89,10 +89,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Span colorBufferRow = this.ColorBuffer.GetRowSpan(yBuffer); Span blockRow = spectralBuffer.GetRowSpan(yBlockStart + y); - // see: https://github.com/SixLabors/ImageSharp/issues/824 - int widthInBlocks = Math.Min(spectralBuffer.Width, this.SizeInBlocks.Width); - - for (int xBlock = 0; xBlock < widthInBlocks; xBlock++) + for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) { // Integer to float workspaceBlock.LoadFrom(ref blockRow[xBlock]); From e8b59c6bb4b8f77714170285ef7710b4c0e4e6db Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sat, 27 Nov 2021 10:11:41 +0300 Subject: [PATCH 09/15] Fixed access modifier & docs --- .../Jpeg/Components/Decoder/IRawJpegData.cs | 1 - .../Decoder/JpegComponentPostProcessor.cs | 52 +++++++++---------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index 0b80acc5d..33815e539 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -5,7 +5,6 @@ using System; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { - /// /// /// Represents decompressed, unprocessed jpeg data with spectral space -s. /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index 35f421263..b87d74720 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -21,6 +21,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// private readonly JpegFrame frame; + /// + /// Gets the maximal number of block rows being processed in one step. + /// + private readonly int blockRowsPerStep; + + /// + /// Gets the component containing decoding meta information. + /// + private readonly IJpegComponent component; + + /// + /// Gets the instance containing decoding meta information. + /// + private readonly IRawJpegData rawJpeg; + /// /// Initializes a new instance of the class. /// @@ -28,39 +43,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { this.frame = frame; - this.Component = component; - this.RawJpeg = rawJpeg; - this.blockAreaSize = this.Component.SubSamplingDivisors * 8; + this.component = component; + this.rawJpeg = rawJpeg; + this.blockAreaSize = this.component.SubSamplingDivisors * 8; this.ColorBuffer = memoryAllocator.Allocate2DOveraligned( postProcessorBufferSize.Width, postProcessorBufferSize.Height, this.blockAreaSize.Height); - this.BlockRowsPerStep = postProcessorBufferSize.Height / 8 / this.Component.SubSamplingDivisors.Height; + this.blockRowsPerStep = postProcessorBufferSize.Height / 8 / this.component.SubSamplingDivisors.Height; } - public IRawJpegData RawJpeg { get; } - - /// - /// Gets the - /// - public IJpegComponent Component { get; } - /// /// Gets the temporary working buffer of color values. /// public Buffer2D ColorBuffer { get; } - /// - /// Gets - /// - public Size SizeInBlocks => this.Component.SizeInBlocks; - - /// - /// Gets the maximal number of block rows being processed in one step. - /// - public int BlockRowsPerStep { get; } - /// public void Dispose() => this.ColorBuffer.Dispose(); @@ -69,20 +67,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// public void CopyBlocksToColorBuffer(int spectralStep) { - Buffer2D spectralBuffer = this.Component.SpectralBlocks; + Buffer2D spectralBuffer = this.component.SpectralBlocks; float maximumValue = this.frame.MaxColorChannelValue; int destAreaStride = this.ColorBuffer.Width; - int yBlockStart = spectralStep * this.BlockRowsPerStep; + int yBlockStart = spectralStep * this.blockRowsPerStep; - Size subSamplingDivisors = this.Component.SubSamplingDivisors; + Size subSamplingDivisors = this.component.SubSamplingDivisors; - Block8x8F dequantTable = this.RawJpeg.QuantizationTables[this.Component.QuantizationTableIndex]; + Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.component.QuantizationTableIndex]; Block8x8F workspaceBlock = default; - for (int y = 0; y < this.BlockRowsPerStep; y++) + for (int y = 0; y < this.blockRowsPerStep; y++) { int yBuffer = y * this.blockAreaSize.Height; @@ -118,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public void ClearSpectralBuffers() { - Buffer2D spectralBlocks = this.Component.SpectralBlocks; + Buffer2D spectralBlocks = this.component.SpectralBlocks; for (int i = 0; i < spectralBlocks.Height; i++) { spectralBlocks.GetRowSpan(i).Clear(); From eb120ba1a516132e8a5679c4a26c807c269e3e8f Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 30 Nov 2021 08:31:23 +0300 Subject: [PATCH 10/15] gfoidl const Size struct jit inlining in color converters --- .../Encoder/LuminanceForwardConverter{TPixel}.cs | 10 +++++----- .../Components/Encoder/RgbForwardConverter{TPixel}.cs | 10 +++++----- .../Encoder/YCbCrForwardConverter420{TPixel}.cs | 10 +++++----- .../Encoder/YCbCrForwardConverter444{TPixel}.cs | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs index b6edfefed..6c402fcfd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs @@ -21,11 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// private const int PixelsPerSample = 8 * 8; - /// - /// of sampling area from given frame pixel buffer. - /// - private static readonly Size SampleSize = new Size(8, 8); - /// /// The Y component /// @@ -62,6 +57,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.config = frame.GetConfiguration(); } + /// + /// Gets size of sampling area from given frame pixel buffer. + /// + private static Size SampleSize => new(8, 8); + /// /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure () /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs index 0be1076e2..789277d7d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs @@ -26,11 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// private const int RgbSpanByteSize = PixelsPerSample * 3; - /// - /// of sampling area from given frame pixel buffer. - /// - private static readonly Size SampleSize = new Size(8, 8); - /// /// The Red component. /// @@ -81,6 +76,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.config = frame.GetConfiguration(); } + /// + /// Gets size of sampling area from given frame pixel buffer. + /// + private static Size SampleSize => new(8, 8); + /// /// Converts a 8x8 image area inside 'pixels' at position (x, y) to Rgb24. /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs index bfeafcbb3..3a878f3c6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs @@ -25,11 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// private const int RgbSpanByteSize = PixelsPerSample * 3; - /// - /// of sampling area from given frame pixel buffer - /// - private static readonly Size SampleSize = new Size(16, 8); - /// /// The left Y component /// @@ -102,6 +97,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } } + /// + /// Gets size of sampling area from given frame pixel buffer. + /// + private static Size SampleSize => new(16, 8); + public void Convert(int x, int y, ref RowOctet currentRows, int idx) { YCbCrForwardConverter.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs index 2dbd1a2dc..5f7725bdd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs @@ -25,11 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// private const int RgbSpanByteSize = PixelsPerSample * 3; - /// - /// of sampling area from given frame pixel buffer - /// - private static readonly Size SampleSize = new Size(8, 8); - /// /// The Y component /// @@ -96,6 +91,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } } + /// + /// Gets size of sampling area from given frame pixel buffer. + /// + private static Size SampleSize => new(8, 8); + /// /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) /// From 15f7889811baf9929931ce35ed509c81ca2e5eba Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 30 Nov 2021 10:36:10 +0300 Subject: [PATCH 11/15] Removed benchmarking code from test suite - we already have those in benchmark project --- .../Formats/Jpg/JpegColorConverterTests.cs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 4e0970e3b..d6dc57e83 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -340,32 +340,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg seed); } - // Benchmark, for local execution only - // [Theory] - // [InlineData(false)] - // [InlineData(true)] - public void BenchmarkYCbCr(bool simd) - { - int count = 2053; - int times = 50000; - - JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); - var result = new Vector4[count]; - - JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrVector4(8) : new JpegColorConverter.FromYCbCrBasic(8); - - // Warm up: - converter.ConvertToRgbInplace(values); - - using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}")) - { - for (int i = 0; i < times; i++) - { - converter.ConvertToRgbInplace(values); - } - } - } - private static JpegColorConverter.ComponentValues CreateRandomValues( int componentCount, int inputBufferLength, From 717b166e41252a36bd04ba528c1b8a7f7238b095 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 30 Nov 2021 10:42:08 +0300 Subject: [PATCH 12/15] Removed unnecessary small allocation from color post processing, removed test benchmark call from sandbox project --- .../ColorConverters/JpegColorConverter.cs | 17 +++++++++++++++++ .../Decoder/JpegComponentPostProcessor.cs | 3 +++ .../Decoder/SpectralConverter{TPixel}.cs | 6 +----- .../Program.cs | 6 ------ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index 11ea4cda8..dad46861e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -197,6 +197,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters /// public readonly Span Component3; + /// + /// Initializes a new instance of the struct. + /// + /// The 1-4 sized list of component post processors. + /// The row to convert + public ComponentValues(IReadOnlyList componentProcessors, int row) + { + this.ComponentCount = componentProcessors.Count; + + this.Component0 = componentProcessors[0].GetColorBufferRowSpan(row); + + // In case of grayscale, Component1 and Component2 point to Component0 memory area + this.Component1 = this.ComponentCount > 1 ? componentProcessors[1].GetColorBufferRowSpan(row) : this.Component0; + this.Component2 = this.ComponentCount > 2 ? componentProcessors[2].GetColorBufferRowSpan(row) : this.Component0; + this.Component3 = this.ComponentCount > 3 ? componentProcessors[3].GetColorBufferRowSpan(row) : Span.Empty; + } + /// /// Initializes a new instance of the struct. /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index b87d74720..3e04e80b7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -122,5 +122,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder spectralBlocks.GetRowSpan(i).Clear(); } } + + public Span GetColorBufferRowSpan(int row) => + this.ColorBuffer.GetRowSpan(row); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index 2e965e0ac..722570919 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.Linq; -using System.Numerics; using System.Threading; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters; using SixLabors.ImageSharp.Memory; @@ -23,7 +22,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private JpegColorConverter colorConverter; - // private IMemoryOwner rgbaBuffer; private IMemoryOwner rgbBuffer; private IMemoryOwner paddedProxyPixelRow; @@ -121,11 +119,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep); - var buffers = new Buffer2D[this.componentProcessors.Length]; for (int i = 0; i < this.componentProcessors.Length; i++) { this.componentProcessors[i].CopyBlocksToColorBuffer(spectralStep); - buffers[i] = this.componentProcessors[i].ColorBuffer; } int width = this.pixelBuffer.Width; @@ -134,7 +130,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { int y = yy - this.pixelRowCounter; - var values = new JpegColorConverter.ComponentValues(buffers, y); + var values = new JpegColorConverter.ComponentValues(this.componentProcessors, y); this.colorConverter.ConvertToRgbInplace(values); values = values.Slice(0, width); // slice away Jpeg padding diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs index e6e82b981..079d24c47 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs @@ -47,12 +47,6 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox benchmarks.EncodeJpeg_SingleMidSize(); } - private static void RunJpegColorProfilingTests() - { - new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(false); - new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(true); - } - private static void RunResizeProfilingTest() { var test = new ResizeProfilingBenchmarks(new ConsoleOutput()); From 13602169c0d1ac194f5efc93db419f32189905ad Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 30 Nov 2021 12:53:34 +0300 Subject: [PATCH 13/15] Moved cancellation token to a more appropriate place --- .../Components/Decoder/SpectralConverter{TPixel}.cs | 11 +++-------- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 7 +++++-- .../Compression/Decompressors/JpegTiffCompression.cs | 6 +++--- .../Decompressors/RgbJpegSpectralConverter.cs | 4 ++-- .../Formats/Jpg/SpectralToPixelConversionTests.cs | 5 +++-- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index 722570919..ad19f476f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { private readonly Configuration configuration; - private readonly CancellationToken cancellationToken; - private JpegComponentPostProcessor[] componentProcessors; private JpegColorConverter colorConverter; @@ -32,13 +30,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private int pixelRowCounter; - public SpectralConverter(Configuration configuration, CancellationToken cancellationToken) - { + public SpectralConverter(Configuration configuration) => this.configuration = configuration; - this.cancellationToken = cancellationToken; - } - public Buffer2D GetPixelBuffer() + public Buffer2D GetPixelBuffer(CancellationToken cancellationToken) { if (!this.Converted) { @@ -46,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int step = 0; step < steps; step++) { - this.cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); this.ConvertStride(step); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 73763f4ab..4be6731cc 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - using var spectralConverter = new SpectralConverter(this.Configuration, cancellationToken); + using var spectralConverter = new SpectralConverter(this.Configuration); var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, cancellationToken); @@ -185,7 +185,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.InitIptcProfile(); this.InitDerivedMetadataProperties(); - return new Image(this.Configuration, spectralConverter.GetPixelBuffer(), this.Metadata); + return new Image( + this.Configuration, + spectralConverter.GetPixelBuffer(cancellationToken), + this.Metadata); } /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs index 9a0607584..c64fc8ad1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs @@ -55,17 +55,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { using var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder()); - // TODO: Should we pass through the CancellationToken from the tiff decoder? // If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space. // There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB). using SpectralConverter spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ? - new RgbJpegSpectralConverter(this.configuration, CancellationToken.None) : new SpectralConverter(this.configuration, CancellationToken.None); + new RgbJpegSpectralConverter(this.configuration) : new SpectralConverter(this.configuration); var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None); jpegDecoder.LoadTables(this.jpegTables, scanDecoder); scanDecoder.ResetInterval = 0; jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None); - CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer()); + // TODO: Should we pass through the CancellationToken from the tiff decoder? + CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None)); } else { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs index aefec7fa3..7b3b67a64 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs @@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// The configuration. /// The cancellation token. - public RgbJpegSpectralConverter(Configuration configuration, CancellationToken cancellationToken) - : base(configuration, cancellationToken) + public RgbJpegSpectralConverter(Configuration configuration) + : base(configuration) { } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs index f0f92d763..0071c623c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; +using System.Threading; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.IO; @@ -43,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); // Decoding - using var converter = new SpectralConverter(Configuration.Default, cancellationToken: default); + using var converter = new SpectralConverter(Configuration.Default); var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); var scanDecoder = new HuffmanScanDecoder(bufferedStream, converter, cancellationToken: default); decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default); @@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg provider.Utility.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName; // Comparison - using (Image image = new Image(Configuration.Default, converter.GetPixelBuffer(), new ImageMetadata())) + using (Image image = new Image(Configuration.Default, converter.GetPixelBuffer(CancellationToken.None), new ImageMetadata())) using (Image referenceImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) { ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); From e9a25c74e0e1b8685306693f6987c38f8fd9201b Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 30 Nov 2021 13:22:48 +0300 Subject: [PATCH 14/15] Added xml docs to the SpectralConverter --- .../Components/Decoder/SpectralConverter.cs | 13 ++- .../Decoder/SpectralConverter{TPixel}.cs | 83 +++++++++++++++---- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs index 4e74f6226..1eb74ff80 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs @@ -6,11 +6,8 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Converter used to convert jpeg spectral data. + /// Converter used to convert jpeg spectral data to color pixels. /// - /// - /// This is tightly coupled with and . - /// internal abstract class SpectralConverter { /// @@ -30,12 +27,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder public abstract void InjectFrameData(JpegFrame frame, IRawJpegData jpegData); /// - /// Called once per spectral stride for each component in . - /// This is called only for baseline interleaved jpegs. + /// Converts single spectral jpeg stride to color stride in baseline + /// decoding mode. /// /// + /// Called once per decoded spectral stride in + /// only for baseline interleaved jpeg images. /// Spectral 'stride' doesn't particularly mean 'single stride'. - /// Actual stride height depends on the subsampling factor of the given component. + /// Actual stride height depends on the subsampling factor of the given image. /// public abstract void ConvertStrideBaseline(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index ad19f476f..0003437e7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -11,28 +11,77 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { + /// + /// + /// Color decoding scheme: + /// + /// + /// 1. Decode spectral data to Jpeg color space + /// 2. Convert from Jpeg color space to RGB + /// 3. Convert from RGB to target pixel space + /// + /// + /// internal class SpectralConverter : SpectralConverter, IDisposable where TPixel : unmanaged, IPixel { + /// + /// instance associated with current + /// decoding routine. + /// private readonly Configuration configuration; + /// + /// Jpeg component converters from decompressed spectral to color data. + /// private JpegComponentPostProcessor[] componentProcessors; + /// + /// Color converter from jpeg color space to target pixel color space. + /// private JpegColorConverter colorConverter; + /// + /// Intermediate buffer of RGB components used in color conversion. + /// private IMemoryOwner rgbBuffer; + /// + /// Proxy buffer used in packing from RGB to target TPixel pixels. + /// private IMemoryOwner paddedProxyPixelRow; + /// + /// Resulting 2D pixel buffer. + /// private Buffer2D pixelBuffer; + /// + /// How many pixel rows are processed in one 'stride'. + /// private int pixelRowsPerStep; + /// + /// How many pixel rows were processed. + /// private int pixelRowCounter; + /// + /// Initializes a new instance of the class. + /// + /// The configuration. public SpectralConverter(Configuration configuration) => this.configuration = configuration; + /// + /// Gets converted pixel buffer. + /// + /// + /// For non-baseline interleaved jpeg this method does a 'lazy' spectral + /// conversion from spectral to color. + /// + /// Cancellation token. + /// Pixel buffer. public Buffer2D GetPixelBuffer(CancellationToken cancellationToken) { if (!this.Converted) @@ -95,21 +144,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } - /// - public void Dispose() - { - if (this.componentProcessors != null) - { - foreach (JpegComponentPostProcessor cpp in this.componentProcessors) - { - cpp.Dispose(); - } - } - - this.rgbBuffer?.Dispose(); - this.paddedProxyPixelRow?.Dispose(); - } - + /// + /// Converts single spectral jpeg stride to color stride. + /// + /// Spectral stride index. private void ConvertStride(int spectralStep) { int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep); @@ -155,5 +193,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.pixelRowCounter += this.pixelRowsPerStep; } + + /// + public void Dispose() + { + if (this.componentProcessors != null) + { + foreach (JpegComponentPostProcessor cpp in this.componentProcessors) + { + cpp.Dispose(); + } + } + + this.rgbBuffer?.Dispose(); + this.paddedProxyPixelRow?.Dispose(); + } } } From 7d524bda2be5eeafef4cd6d5d723e93159a50092 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Tue, 30 Nov 2021 14:03:39 +0300 Subject: [PATCH 15/15] Fixed compilation error --- .../Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs index 7b3b67a64..3b5833c10 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs @@ -21,7 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// This Spectral converter will always convert the pixel data to RGB color. /// /// The configuration. - /// The cancellation token. public RgbJpegSpectralConverter(Configuration configuration) : base(configuration) {