diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs index 83aae3c946..3f4c69c3ed 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -16,8 +16,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// public const int Size = 64; + /// + /// A fixed size buffer holding the values. + /// See: + /// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers + /// + /// private fixed short data[Size]; + /// + /// Initializes a new instance of the struct. + /// + /// A of coefficients public Block8x8(Span coefficients) { ref byte selfRef = ref Unsafe.As(ref this); @@ -25,6 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short)); } + /// + /// Gets or sets a value at the given index + /// + /// The index + /// The value public short this[int idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -44,6 +59,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common } } + /// + /// Gets or sets a value in a row+coulumn of the 8x8 block + /// + /// The x position index in the row + /// The column index + /// The value public short this[int x, int y] { get => this[(y * 8) + x]; @@ -60,6 +81,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return !left.Equals(right); } + /// + /// Multiply all elements by a given + /// public static Block8x8 operator *(Block8x8 block, int value) { Block8x8 result = block; @@ -73,6 +97,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return result; } + /// + /// Divide all elements by a given + /// public static Block8x8 operator /(Block8x8 block, int value) { Block8x8 result = block; @@ -86,6 +113,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return result; } + /// + /// Add an to all elements + /// public static Block8x8 operator +(Block8x8 block, int value) { Block8x8 result = block; @@ -99,6 +129,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return result; } + /// + /// Subtract an from all elements + /// public static Block8x8 operator -(Block8x8 block, int value) { Block8x8 result = block; @@ -142,6 +175,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common fp[idx] = value; } + /// + /// Convert into + /// public Block8x8F AsFloatBlock() { // TODO: Optimize this @@ -154,6 +190,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return result; } + /// + /// Copy all elements to an array of . + /// public short[] ToArray() { short[] result = new short[Size]; @@ -161,6 +200,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return result; } + /// + /// Copy elements into 'destination' Span of values + /// public void CopyTo(Span destination) { ref byte selfRef = ref Unsafe.As(ref this); @@ -168,6 +210,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short)); } + /// + /// Copy elements into 'destination' Span of values + /// public void CopyTo(Span destination) { for (int i = 0; i < Size; i++) @@ -176,6 +221,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common } } + /// + /// Cast and copy -s from the beginning of 'source' span. + /// public void LoadFrom(Span source) { for (int i = 0; i < Size; i++) @@ -191,6 +239,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx)); } + /// public override string ToString() { var bld = new StringBuilder(); @@ -208,6 +257,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return bld.ToString(); } + /// public bool Equals(Block8x8 other) { for (int i = 0; i < Size; i++) @@ -221,6 +271,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return true; } + /// public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) @@ -231,11 +282,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return obj is Block8x8 && this.Equals((Block8x8)obj); } + /// public override int GetHashCode() { return (this[0] * 31) + this[1]; } + /// + /// Calculate the total sum of absoulute differences of elements in 'a' and 'b'. + /// public static long TotalDifference(ref Block8x8 a, ref Block8x8 b) { long result = 0; diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs index d513401864..da97f9e2ae 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs @@ -5,6 +5,9 @@ /// internal static class ComponentUtils { + /// + /// Gets a reference to the at the given row and column index from + /// public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by) { return ref component.SpectralBlocks[bx, by]; diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs index b83a05c6ac..5677134225 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs @@ -7,17 +7,32 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder { + /// + /// Encapsulates the conversion of Jpeg channels to RGBA values packed in buffer. + /// internal abstract partial class JpegColorConverter { + /// + /// The avalilable converters + /// private static readonly JpegColorConverter[] Converters = { new FromYCbCr(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() }; + /// + /// Initializes a new instance of the class. + /// protected JpegColorConverter(JpegColorSpace colorSpace) { this.ColorSpace = colorSpace; } + /// + /// Gets the of this converter. + /// public JpegColorSpace ColorSpace { get; } + /// + /// Returns the corresponding to the given + /// public static JpegColorConverter GetConverter(JpegColorSpace colorSpace) { JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace); @@ -29,20 +44,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder return converter; } + /// + /// He implementation of the conversion. + /// + /// The input as a stack-only struct + /// The destination buffer of values public abstract void ConvertToRGBA(ComponentValues values, Span result); + /// + /// A stack-only struct to reference the input buffers using -s. + /// public struct ComponentValues { + /// + /// The component count + /// public readonly int ComponentCount; + /// + /// The component 0 (eg. Y) + /// public readonly ReadOnlySpan Component0; + /// + /// The component 1 (eg. Cb) + /// public readonly ReadOnlySpan Component1; + /// + /// The component 2 (eg. Cr) + /// public readonly ReadOnlySpan Component2; + /// + /// The component 4 + /// public readonly ReadOnlySpan Component3; + /// + /// Initializes a new instance of the struct. + /// + /// The 1-4 sized list of component buffers. + /// The row to convert public ComponentValues(IReadOnlyList> componentBuffers, int row) { this.ComponentCount = componentBuffers.Count; diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs index 2c60728fd7..feb5164d73 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs @@ -1,16 +1,27 @@ using System; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; using SixLabors.ImageSharp.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder { + /// + /// Encapsulates postprocessing data for one component for . + /// internal class JpegComponentPostProcessor : IDisposable { + /// + /// Points to the current row in . + /// private int currentComponentRowInBlocks; + /// + /// The size of the area in corrsponding to one 8x8 Jpeg block + /// private readonly Size blockAreaSize; + /// + /// Initializes a new instance of the class. + /// public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component) { this.Component = component; @@ -21,21 +32,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder this.blockAreaSize = this.Component.SubSamplingDivisors * 8; } + /// + /// Gets the + /// public JpegImagePostProcessor ImagePostProcessor { get; } + /// + /// Gets the + /// public IJpegComponent Component { get; } + /// + /// Gets the temporal 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(); } + /// + /// Invoke for block rows, copy the result into . + /// public unsafe void CopyBlocksToColorBuffer() { var blockPp = default(JpegBlockPostProcessor); diff --git a/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs index b51cd203dd..b9bfe425ab 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs @@ -8,10 +8,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// internal static class SizeExtensions { + /// + /// Multiplies 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'. + /// TODO: Shouldn't we expose this as operator in SixLabors.Core? + /// public static Size MultiplyBy(this Size a, Size b) => new Size(a.Width * b.Width, a.Height * b.Height); + /// + /// Divides 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'. + /// TODO: Shouldn't we expose this as operator in SixLabors.Core? + /// public static Size DivideBy(this Size a, Size b) => new Size(a.Width / b.Width, a.Height / b.Height); + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// public static Size DivideRoundUp(this Size originalSize, int divX, int divY) { var sizeVect = (Vector2)(SizeF)originalSize; @@ -22,9 +33,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return new Size((int)sizeVect.X, (int)sizeVect.Y); } + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// public static Size DivideRoundUp(this Size originalSize, int divisor) => DivideRoundUp(originalSize, divisor, divisor); + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// public static Size DivideRoundUp(this Size originalSize, Size divisor) => DivideRoundUp(originalSize, divisor.Width, divisor.Height); } diff --git a/src/ImageSharp/Memory/Buffer2D.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs similarity index 100% rename from src/ImageSharp/Memory/Buffer2D.cs rename to src/ImageSharp/Memory/Buffer2D{T}.cs diff --git a/src/ImageSharp/Memory/BufferArea.cs b/src/ImageSharp/Memory/BufferArea{T}.cs similarity index 62% rename from src/ImageSharp/Memory/BufferArea.cs rename to src/ImageSharp/Memory/BufferArea{T}.cs index 0e14d1eacf..92e78e9c07 100644 --- a/src/ImageSharp/Memory/BufferArea.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -12,6 +12,9 @@ namespace SixLabors.ImageSharp.Memory internal struct BufferArea where T : struct { + /// + /// The rectangle specifying the boundaries of the area in . + /// public readonly Rectangle Rectangle; public BufferArea(IBuffer2D destinationBuffer, Rectangle rectangle) @@ -30,17 +33,41 @@ namespace SixLabors.ImageSharp.Memory { } + /// + /// Gets the being pointed by this instance. + /// public IBuffer2D DestinationBuffer { get; } + /// + /// Gets the size of the area. + /// public Size Size => this.Rectangle.Size; + /// + /// Gets the pixel stride which is equal to the width of . + /// public int Stride => this.DestinationBuffer.Width; + /// + /// Gets or sets a value at the given index. + /// + /// The position inside a row + /// The row index + /// The reference to the value public ref T this[int x, int y] => ref this.DestinationBuffer.Span[this.GetIndexOf(x, y)]; + /// + /// Gets a reference to the [0,0] element. + /// + /// The reference to the [0,0] element public ref T GetReferenceToOrigo() => ref this.DestinationBuffer.Span[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; + /// + /// Gets a span to row 'y' inside this area. + /// + /// The row index + /// The span [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetRowSpan(int y) { @@ -51,6 +78,14 @@ namespace SixLabors.ImageSharp.Memory return this.DestinationBuffer.Span.Slice(yy + xx, width); } + /// + /// Returns a sub-area as . (Similar to .) + /// + /// The x index at the subarea origo + /// The y index at the subarea origo + /// The desired width of the subarea + /// The desired height of the subarea + /// The subarea [MethodImpl(MethodImplOptions.AggressiveInlining)] public BufferArea GetSubArea(int x, int y, int width, int height) { @@ -58,6 +93,11 @@ namespace SixLabors.ImageSharp.Memory return this.GetSubArea(rectangle); } + /// + /// Returns a sub-area as . (Similar to .) + /// + /// The specifying the boundaries of the subarea + /// The subarea [MethodImpl(MethodImplOptions.AggressiveInlining)] public BufferArea GetSubArea(Rectangle rectangle) { diff --git a/src/ImageSharp/Memory/Buffer.cs b/src/ImageSharp/Memory/Buffer{T}.cs similarity index 100% rename from src/ImageSharp/Memory/Buffer.cs rename to src/ImageSharp/Memory/Buffer{T}.cs diff --git a/src/ImageSharp/Memory/IBuffer2D.cs b/src/ImageSharp/Memory/IBuffer2D{T}.cs similarity index 100% rename from src/ImageSharp/Memory/IBuffer2D.cs rename to src/ImageSharp/Memory/IBuffer2D{T}.cs diff --git a/src/ImageSharp/Memory/IBuffer.cs b/src/ImageSharp/Memory/IBuffer{T}.cs similarity index 90% rename from src/ImageSharp/Memory/IBuffer.cs rename to src/ImageSharp/Memory/IBuffer{T}.cs index f59c5d5ea5..a0f80063f8 100644 --- a/src/ImageSharp/Memory/IBuffer.cs +++ b/src/ImageSharp/Memory/IBuffer{T}.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Memory { /// /// - /// Represents a contigous memory buffer of value-type items "promising" a + /// Represents a contigous memory buffer of value-type items "promising" a /// /// The value type internal interface IBuffer : IDisposable diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index d8cb8af8cb..9a1a89b3e1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -41,6 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } + // TODO: This test occasionally fails. Don't get the reason, BufferArea.CopyTo() is totally OK. [Fact] public void Unscaled() { @@ -61,6 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } + // TODO: This test occasionally fails. Don't get the reason, BufferArea.CopyTo() is totally OK. [Theory] [InlineData(1, 1)] [InlineData(1, 2)] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 8ccd2f63c1..40b41b9cba 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -13,6 +13,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils internal static partial class LibJpegTools { + /// + /// Stores spectral blocks for jpeg components. + /// public class ComponentData : IEquatable, IJpegComponent { public ComponentData(int widthInBlocks, int heightInBlocks, int index) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index e18a5a285f..ae7a9c046f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -12,6 +12,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils internal static partial class LibJpegTools { + /// + /// Stores spectral jpeg compoent data in libjpeg-compatible style. + /// public class SpectralData : IEquatable { public int ComponentCount { get; private set; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs index 90fb1cc297..5875110202 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs @@ -8,6 +8,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils using SixLabors.ImageSharp.Formats.Jpeg.Common; + /// + /// Utilities to read raw libjpeg data for reference conversion. + /// internal static partial class LibJpegTools { public static (double total, double average) CalculateDifference(ComponentData expected, ComponentData actual) @@ -47,13 +50,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils TestEnvironment.ToolsDirectoryFullPath, @"jpeg\dump-jpeg-coeffs.exe"); + /// + /// Executes 'dump-jpeg-coeffs.exe' for the given jpeg image file, saving the libjpeg spectral data into 'destFile'. Windows only! + /// See: + /// + /// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md + /// + /// public static void RunDumpJpegCoeffsTool(string sourceFile, string destFile) { + if (!TestEnvironment.IsWindows) + { + throw new InvalidOperationException("Can't run dump-jpeg-coeffs.exe in non-Windows environment. Skip this test on Linux/Unix!"); + } + string args = $@"""{sourceFile}"" ""{destFile}"""; var process = Process.Start(DumpToolFullPath, args); process.WaitForExit(); } + /// + /// Extract libjpeg from the given jpg file with 'dump-jpeg-coeffs.exe'. Windows only! + /// See: + /// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md + /// public static SpectralData ExtractSpectralData(string inputFile) { TestFile testFile = TestFile.Create(inputFile); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs index eeb9aacb44..ef9a73d12d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -71,6 +71,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils return r; } +#pragma warning disable 219 + /// /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200 /// @@ -82,6 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils float z0, z1, z2, z3, z4; // see: PrintConstants() + float r0 = 1.41421354f; float r1 = 1.3870399f; float r2 = 1.306563f; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs index 4d2a1e44ff..9afc4b0b3b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs @@ -8,6 +8,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils internal static partial class ReferenceImplementations { /// + /// TODO: produces really bad results for bigger values! + /// /// Contains the "original" golang based DCT/IDCT implementations as reference implementations. /// 1. ===== Forward DCT ===== /// **** The original golang source claims: @@ -76,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils return result; } - [Obsolete("Looks like this method produces really bad results for bigger values!")] + // [Obsolete("Looks like this method produces really bad results for bigger values!")] public static Block8x8 TransformIDCT(ref Block8x8 block) { int[] temp = new int[Block8x8.Size]; @@ -233,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. /// /// The source block of coefficients - [Obsolete("Looks like this method produces really bad results for bigger values!")] + // [Obsolete("Looks like this method produces really bad results for bigger values!")] public static void TransformIDCTInplace(Span src) { // Horizontal 1-D IDCT.