diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs similarity index 99% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs index 0e42d074c..f4527966a 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; using SixLabors.Memory; // TODO: This could be useful elsewhere. -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// A stream reader that add a secondary level buffer in addition to native stream buffered reading diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs index 0fc85c6e4..6d06abecf 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.CompilerServices; using SixLabors.Memory; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// The collection of lookup tables used for fast AC entropy scan decoding. @@ -35,10 +35,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } /// - /// Gets a reference to the first element of the AC table indexed by - /// + /// Gets a reference to the first element of the AC table indexed by /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetAcTableReference(PdfJsFrameComponent component) + public ref short GetAcTableReference(JpegFrameComponent component) { return ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; } @@ -48,11 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// The table index. /// The collection of AC Huffman tables. - public void BuildACTableLut(int index, PdfJsHuffmanTables acHuffmanTables) + public void BuildACTableLut(int index, HuffmanTables acHuffmanTables) { const int FastBits = ScanDecoder.FastBits; Span fastAC = this.tables.GetRowSpan(index); - ref PdfJsHuffmanTable huffman = ref acHuffmanTables[index]; + ref HuffmanTable huffman = ref acHuffmanTables[index]; int i; for (i = 0; i < (1 << FastBits); i++) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs index 5870e3da8..1d26178e0 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedByteBuffer256 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs index c509903c9..556e74fd5 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedByteBuffer512 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs index b304dbf8c..a3b67a700 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedInt16Buffer257 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs index f8507ec47..bba89f072 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedInt32Buffer18 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs index 9b076d9da..1d3ca9933 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedUInt32Buffer18 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 15ae56331..c0f3b17cd 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -6,13 +6,13 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.Memory; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Represents a Huffman Table /// [StructLayout(LayoutKind.Sequential)] - internal unsafe struct PdfJsHuffmanTable + internal unsafe struct HuffmanTable { /// /// Gets the max code array @@ -40,12 +40,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components public FixedInt16Buffer257 Sizes; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The to use for buffer allocations. /// The code lengths /// The huffman values - public PdfJsHuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan count, ReadOnlySpan values) + public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan count, ReadOnlySpan values) { const int Length = 257; using (IBuffer huffcode = memoryAllocator.Allocate(Length)) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs similarity index 54% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs index 5cbde2b88..dc066aa0a 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs @@ -1,24 +1,23 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Defines a 2 pairs of huffman tables + /// Defines a 2 pairs of huffman tables. /// - internal sealed class PdfJsHuffmanTables + internal sealed class HuffmanTables { - private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4]; + private readonly HuffmanTable[] tables = new HuffmanTable[4]; /// /// Gets or sets the table at the given index. /// /// The index - /// The - public ref PdfJsHuffmanTable this[int index] + /// The + public ref HuffmanTable this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref this.tables[index]; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs similarity index 79% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs index 85c9f9466..31f4efdcb 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs @@ -3,19 +3,19 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Represents a jpeg file marker + /// Represents a jpeg file marker. /// - internal readonly struct PdfJsFileMarker + internal readonly struct JpegFileMarker { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The marker /// The position within the stream - public PdfJsFileMarker(byte marker, long position) + public JpegFileMarker(byte marker, long position) { this.Marker = marker; this.Position = position; @@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The marker /// The position within the stream /// Whether the current marker is invalid - public PdfJsFileMarker(byte marker, long position, bool invalid) + public JpegFileMarker(byte marker, long position, bool invalid) { this.Marker = marker; this.Position = position; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index 8ce981a09..a238e0734 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -3,12 +3,12 @@ using System; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Represent a single jpeg frame /// - internal sealed class PdfJsFrame : IDisposable + internal sealed class JpegFrame : IDisposable { /// /// Gets or sets a value indicating whether the frame uses the extended specification @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Gets or sets the frame component collection /// - public PdfJsFrameComponent[] Components { get; set; } + public JpegFrameComponent[] Components { get; set; } /// /// Gets or sets the maximum horizontal sampling factor @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < this.ComponentCount; i++) { - PdfJsFrameComponent component = this.Components[i]; + JpegFrameComponent component = this.Components[i]; component.Init(); } } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs index 7d4eb66e8..3ce7cacfa 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs @@ -5,21 +5,19 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Represents a single frame component /// - internal class PdfJsFrameComponent : IDisposable, IJpegComponent + internal class JpegFrameComponent : IDisposable, IJpegComponent { private readonly MemoryAllocator memoryAllocator; - public PdfJsFrameComponent(MemoryAllocator memoryAllocator, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) + public JpegFrameComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) { this.memoryAllocator = memoryAllocator; this.Frame = frame; @@ -89,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// public int ACHuffmanTableId { get; set; } - public PdfJsFrame Frame { get; } + public JpegFrame Frame { get; } /// public void Dispose() @@ -125,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } else { - PdfJsFrameComponent c0 = this.Frame.Components[0]; + JpegFrameComponent c0 = this.Frame.Components[0]; this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 8575bac69..5afe6382f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Decodes the Huffman encoded spectral scan. @@ -23,13 +21,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components // LUT Bias[n] = (-1 << n) + 1 private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; - private readonly PdfJsFrame frame; - private readonly PdfJsHuffmanTables dcHuffmanTables; - private readonly PdfJsHuffmanTables acHuffmanTables; + private readonly JpegFrame frame; + private readonly HuffmanTables dcHuffmanTables; + private readonly HuffmanTables acHuffmanTables; private readonly FastACTables fastACTables; private readonly DoubleBufferedStreamReader stream; - private readonly PdfJsFrameComponent[] components; + private readonly JpegFrameComponent[] components; private readonly ZigZag dctZigZag; // The restart interval. @@ -97,9 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// The successive approximation bit low end. public ScanDecoder( DoubleBufferedStreamReader stream, - PdfJsFrame frame, - PdfJsHuffmanTables dcHuffmanTables, - PdfJsHuffmanTables acHuffmanTables, + JpegFrame frame, + HuffmanTables dcHuffmanTables, + HuffmanTables acHuffmanTables, FastACTables fastACTables, int componentIndex, int componentsLength, @@ -177,10 +175,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - PdfJsFrameComponent component = this.components[k]; + JpegFrameComponent component = this.components[k]; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -231,13 +229,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// private void ParseBaselineDataNonInterleaved() { - PdfJsFrameComponent component = this.components[this.componentIndex]; + JpegFrameComponent component = this.components[this.componentIndex]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); int mcu = 0; @@ -296,8 +294,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - PdfJsFrameComponent component = this.components[k]; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + JpegFrameComponent component = this.components[k]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -345,13 +343,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// private void ParseProgressiveDataNonInterleaved() { - PdfJsFrameComponent component = this.components[this.componentIndex]; + JpegFrameComponent component = this.components[this.componentIndex]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); int mcu = 0; @@ -396,11 +394,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } private void DecodeBlockBaseline( - PdfJsFrameComponent component, + JpegFrameComponent component, int row, int col, - ref PdfJsHuffmanTable dcTable, - ref PdfJsHuffmanTable acTable, + ref HuffmanTable dcTable, + ref HuffmanTable acTable, ref short fastACRef) { this.CheckBits(); @@ -475,10 +473,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } private void DecodeBlockProgressiveDC( - PdfJsFrameComponent component, + JpegFrameComponent component, int row, int col, - ref PdfJsHuffmanTable dcTable) + ref HuffmanTable dcTable) { if (this.spectralEnd != 0) { @@ -511,10 +509,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } private void DecodeBlockProgressiveAC( - PdfJsFrameComponent component, + JpegFrameComponent component, int row, int col, - ref PdfJsHuffmanTable acTable, + ref HuffmanTable acTable, ref short fastACRef) { if (this.spectralStart == 0) @@ -603,7 +601,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } } - private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref PdfJsHuffmanTable acTable) + private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref HuffmanTable acTable) { int k; @@ -805,7 +803,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } [MethodImpl(InliningOptions.ShortMethod)] - private int DecodeHuffman(ref PdfJsHuffmanTable table) + private int DecodeHuffman(ref HuffmanTable table) { this.CheckBits(); @@ -830,7 +828,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } [MethodImpl(InliningOptions.ColdPath)] - private int DecodeHuffmanSlow(ref PdfJsHuffmanTable table) + private int DecodeHuffmanSlow(ref HuffmanTable table) { // Naive test is to shift the code_buffer down so k bits are // valid, then test against MaxCode. To speed this up, we've @@ -941,7 +939,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < this.components.Length; i++) { - PdfJsFrameComponent c = this.components[i]; + JpegFrameComponent c = this.components[i]; c.DcPredictor = 0; } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs deleted file mode 100644 index 353eb01fe..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Holds the unprocessed bits that have been taken from the byte-stream. - /// The n least significant bits of a form the unread bits, to be read in MSB to - /// LSB order. - /// - internal struct Bits - { - /// - /// Gets or sets the accumulator. - /// - public int Accumulator; - - /// - /// Gets or sets the mask. - /// 0, with mask==0 when unreadbits==0.]]> - /// - public int Mask; - - /// - /// Gets or sets the number of unread bits in the accumulator. - /// - public int UnreadBits; - - /// - /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at - /// least n. For best performance (avoiding function calls inside hot loops), - /// the caller is the one responsible for first checking that bits.UnreadBits < n. - /// - /// The number of bits to ensure. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void EnsureNBits(int n, ref InputProcessor inputProcessor) - { - GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); - errorCode.EnsureNoError(); - } - - /// - /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at - /// least n. For best performance (avoiding function calls inside hot loops), - /// the caller is the one responsible for first checking that bits.UnreadBits < n. - /// This method does not throw. Returns instead. - /// - /// The number of bits to ensure. - /// The - /// Error code - public GolangDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) - { - while (true) - { - GolangDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); - if (errorCode != GolangDecoderErrorCode.NoError || this.UnreadBits >= n) - { - return errorCode; - } - } - } - - /// - /// Unrolled version of for n==8 - /// - /// The - /// A - public GolangDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) - { - return this.EnsureBitsStepImpl(ref inputProcessor); - } - - /// - /// Unrolled version of for n==1 - /// - /// The - /// A - public GolangDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) - { - return this.EnsureBitsStepImpl(ref inputProcessor); - } - - /// - /// Receive extend - /// - /// Byte - /// The - /// Read bits value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReceiveExtend(int t, ref InputProcessor inputProcessor) - { - GolangDecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out int x); - errorCode.EnsureNoError(); - return x; - } - - /// - /// Receive extend - /// - /// Byte - /// The - /// Read bits value - /// The - public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) - { - if (this.UnreadBits < t) - { - GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); - if (errorCode != GolangDecoderErrorCode.NoError) - { - x = int.MaxValue; - return errorCode; - } - } - - this.UnreadBits -= t; - this.Mask >>= t; - int s = 1 << t; - x = (this.Accumulator >> this.UnreadBits) & (s - 1); - - if (x < (s >> 1)) - { - x += ((-1) << t) + 1; - } - - return GolangDecoderErrorCode.NoError; - } - - private GolangDecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) - { - GolangDecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out int c); - - if (errorCode != GolangDecoderErrorCode.NoError) - { - return errorCode; - } - - this.Accumulator = (this.Accumulator << 8) | c; - this.UnreadBits += 8; - if (this.Mask == 0) - { - this.Mask = 1 << 7; - } - else - { - this.Mask <<= 8; - } - - return errorCode; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs deleted file mode 100644 index c8c68aa7e..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Bytes is a byte buffer, similar to a stream, except that it - /// has to be able to unread more than 1 byte, due to byte stuffing. - /// Byte stuffing is specified in section F.1.2.3. - /// TODO: Optimize buffer management inside this class! - /// - internal struct Bytes : IDisposable - { - /// - /// Specifies the buffer size for and - /// - public const int BufferSize = 4096; - - /// - /// Gets or sets the buffer. - /// buffer[i:j] are the buffered bytes read from the underlying - /// stream that haven't yet been passed further on. - /// - public byte[] Buffer; - - /// - /// Values of converted to -s - /// - public int[] BufferAsInt; - - /// - /// Start of bytes read - /// - public int I; - - /// - /// End of bytes read - /// - public int J; - - /// - /// Gets or sets the unreadable bytes. The number of bytes to back up i after - /// overshooting. It can be 0, 1 or 2. - /// - public int UnreadableBytes; - - /// - /// Creates a new instance of the , and initializes it's buffer. - /// - /// The bytes created - public static Bytes Create() - { - // DO NOT bother with buffers and array pooling here! - // It only makes things worse! - return new Bytes - { - Buffer = new byte[BufferSize], - BufferAsInt = new int[BufferSize] - }; - } - - /// - /// Disposes of the underlying buffer - /// - public void Dispose() - { - this.Buffer = null; - this.BufferAsInt = null; - } - - /// - /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. - /// - /// Input stream - /// The result byte as - /// The - public GolangDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) - { - // Take the fast path if bytes.buf contains at least two bytes. - if (this.I + 2 <= this.J) - { - x = this.BufferAsInt[this.I]; - this.I++; - this.UnreadableBytes = 1; - if (x != JpegConstants.Markers.XFFInt) - { - return GolangDecoderErrorCode.NoError; - } - - if (this.BufferAsInt[this.I] != 0x00) - { - return GolangDecoderErrorCode.MissingFF00; - } - - this.I++; - this.UnreadableBytes = 2; - x = JpegConstants.Markers.XFF; - return GolangDecoderErrorCode.NoError; - } - - this.UnreadableBytes = 0; - - GolangDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); - this.UnreadableBytes = 1; - if (errorCode != GolangDecoderErrorCode.NoError) - { - return errorCode; - } - - if (x != JpegConstants.Markers.XFF) - { - return GolangDecoderErrorCode.NoError; - } - - errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); - this.UnreadableBytes = 2; - if (errorCode != GolangDecoderErrorCode.NoError) - { - return errorCode; - } - - if (x != 0x00) - { - return GolangDecoderErrorCode.MissingFF00; - } - - x = JpegConstants.Markers.XFF; - return GolangDecoderErrorCode.NoError; - } - - /// - /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. - /// - /// Input stream - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte(Stream inputStream) - { - GolangDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result); - errorCode.EnsureNoError(); - return result; - } - - /// - /// Extracts the next byte, whether buffered or not buffered into the result out parameter. It does not care about byte stuffing. - /// This method does not throw on format error, it returns a instead. - /// - /// Input stream - /// The result as out parameter - /// The - public GolangDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) - { - GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError; - while (this.I == this.J) - { - errorCode = this.FillUnsafe(inputStream); - if (errorCode != GolangDecoderErrorCode.NoError) - { - result = 0; - return errorCode; - } - } - - result = this.Buffer[this.I]; - this.I++; - this.UnreadableBytes = 0; - return errorCode; - } - - /// - /// Same as but the result is an - /// - /// The input stream - /// The result - /// A - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GolangDecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) - { - GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError; - while (this.I == this.J) - { - errorCode = this.FillUnsafe(inputStream); - if (errorCode != GolangDecoderErrorCode.NoError) - { - result = 0; - return errorCode; - } - } - - result = this.BufferAsInt[this.I]; - this.I++; - this.UnreadableBytes = 0; - return errorCode; - } - - /// - /// Fills up the bytes buffer from the underlying stream. - /// It should only be called when there are no unread bytes in bytes. - /// - /// Thrown when reached end of stream unexpectedly. - /// Input stream - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Fill(Stream inputStream) - { - GolangDecoderErrorCode errorCode = this.FillUnsafe(inputStream); - errorCode.EnsureNoError(); - } - - /// - /// Fills up the bytes buffer from the underlying stream. - /// It should only be called when there are no unread bytes in bytes. - /// This method does not throw , returns a instead! - /// - /// Input stream - /// The - public GolangDecoderErrorCode FillUnsafe(Stream inputStream) - { - if (this.I != this.J) - { - // Unrecoverable error in the input, throwing! - DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist(); - } - - // Move the last 2 bytes to the start of the buffer, in case we need - // to call UnreadByteStuffedByte. - if (this.J > 2) - { - this.Buffer[0] = this.Buffer[this.J - 2]; - this.Buffer[1] = this.Buffer[this.J - 1]; - this.I = 2; - this.J = 2; - } - - // Fill in the rest of the buffer. - int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); - if (n == 0) - { - return GolangDecoderErrorCode.UnexpectedEndOfStream; - } - - this.J += n; - - for (int i = 0; i < this.Buffer.Length; i++) - { - this.BufferAsInt[i] = this.Buffer[i]; - } - - return GolangDecoderErrorCode.NoError; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs deleted file mode 100644 index 2b2bc61ba..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Encapsulates exception thrower methods for the Jpeg Encoder - /// - internal static class DecoderThrowHelper - { - /// - /// Throws an exception that belongs to the given - /// - /// The - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowExceptionForErrorCode(this GolangDecoderErrorCode errorCode) - { - // REMARK: If this method throws for an image that is expected to be decodable, - // consider using the ***Unsafe variant of the parsing method that asks for ThrowExceptionForErrorCode() - // then verify the error code + implement fallback logic manually! - switch (errorCode) - { - case GolangDecoderErrorCode.NoError: - throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); - case GolangDecoderErrorCode.MissingFF00: - throw new MissingFF00Exception(); - case GolangDecoderErrorCode.UnexpectedEndOfStream: - throw new EOFException(); - default: - throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); - } - } - - /// - /// Throws an exception if the given defines an error. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoError(this GolangDecoderErrorCode errorCode) - { - if (errorCode != GolangDecoderErrorCode.NoError) - { - ThrowExceptionForErrorCode(errorCode); - } - } - - /// - /// Throws an exception if the given is . - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoEOF(this GolangDecoderErrorCode errorCode) - { - if (errorCode == GolangDecoderErrorCode.UnexpectedEndOfStream) - { - errorCode.ThrowExceptionForErrorCode(); - } - } - - /// - /// Encapsulates methods throwing different flavours of -s. - /// - public static class ThrowImageFormatException - { - /// - /// Throws "Fill called when unread bytes exist". - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FillCalledWhenUnreadBytesExist() - { - throw new ImageFormatException("Fill called when unread bytes exist!"); - } - - /// - /// Throws "Bad Huffman code". - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void BadHuffmanCode() - { - throw new ImageFormatException("Bad Huffman code!"); - } - - /// - /// Throws "Uninitialized Huffman table". - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void UninitializedHuffmanTable() - { - throw new ImageFormatException("Uninitialized Huffman table"); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs deleted file mode 100644 index 60d9b1e1a..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// The EOF (End of File exception). - /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker - /// TODO: Rename to UnexpectedEndOfStreamException - /// - internal class EOFException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public EOFException() - : base("Reached end of stream before proceeding EOI marker!") - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs deleted file mode 100644 index 72213eb38..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// - /// Represents a single color component - /// - internal class GolangComponent : IDisposable, IJpegComponent - { - public GolangComponent(byte identifier, int index) - { - this.Identifier = identifier; - this.Index = index; - } - - /// - /// Gets the identifier - /// - public byte Identifier { get; } - - /// - public int Index { get; } - - public Size SizeInBlocks { get; private set; } - - public Size SamplingFactors { get; private set; } - - public Size SubSamplingDivisors { get; private set; } - - public int HorizontalSamplingFactor => this.SamplingFactors.Width; - - public int VerticalSamplingFactor => this.SamplingFactors.Height; - - /// - public int QuantizationTableIndex { get; private set; } - - /// - /// - /// Gets the storing the "raw" frequency-domain decoded blocks. - /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. - /// This is done by . - /// When us true, we are touching these blocks multiple times - each time we process a Scan. - /// - public Buffer2D SpectralBlocks { get; private set; } - - /// - /// Initializes - /// - /// The to use for buffer allocations. - /// The instance - public void InitializeDerivedData(MemoryAllocator memoryAllocator, GolangJpegDecoderCore decoder) - { - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - this.SizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(this.SamplingFactors); - - if (this.Index == 0 || this.Index == 3) - { - this.SubSamplingDivisors = new Size(1, 1); - } - else - { - GolangComponent c0 = decoder.Components[0]; - this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); - } - - this.SpectralBlocks = memoryAllocator.Allocate2D(this.SizeInBlocks.Width, this.SizeInBlocks.Height, AllocationOptions.Clean); - } - - /// - /// Initializes all component data except . - /// - /// The instance - public void InitializeCoreData(GolangJpegDecoderCore decoder) - { - // Section B.2.2 states that "the value of C_i shall be different from - // the values of C_1 through C_(i-1)". - int i = this.Index; - - for (int j = 0; j < this.Index; j++) - { - if (this.Identifier == decoder.Components[j].Identifier) - { - throw new ImageFormatException("Repeated component identifier"); - } - } - - this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)]; - if (this.QuantizationTableIndex > GolangJpegDecoderCore.MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - byte hv = decoder.Temp[7 + (3 * i)]; - int h = hv >> 4; - int v = hv & 0x0f; - if (h < 1 || h > 4 || v < 1 || v > 4) - { - throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio"); - } - - if (h == 3 || v == 3) - { - throw new ImageFormatException("Lnsupported subsampling ratio"); - } - - switch (decoder.ComponentCount) - { - case 1: - - // If a JPEG image has only one component, section A.2 says "this data - // is non-interleaved by definition" and section A.2.2 says "[in this - // case...] the order of data units within a scan shall be left-to-right - // and top-to-bottom... regardless of the values of H_1 and V_1". Section - // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be - // one data unit". Similarly, section A.1.1 explains that it is the ratio - // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale - // images, H_1 is the maximum H_j for all components j, so that ratio is - // always 1. The component's (h, v) is effectively always (1, 1): even if - // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 - // MCUs, not two 16x8 MCUs. - h = 1; - v = 1; - break; - - case 3: - - // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, - // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the - // (h, v) values for the Y component are either (1, 1), (1, 2), - // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values - // must be a multiple of the Cb and Cr component's values. We also - // assume that the two chroma components have the same subsampling - // ratio. - switch (i) - { - case 0: - { - // Y. - // We have already verified, above, that h and v are both - // either 1, 2 or 4, so invalid (h, v) combinations are those - // with v == 4. - if (v == 4) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 1: - { - // Cb. - Size s0 = decoder.Components[0].SamplingFactors; - - if (s0.Width % h != 0 || s0.Height % v != 0) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 2: - { - // Cr. - Size s1 = decoder.Components[1].SamplingFactors; - - if (s1.Width != h || s1.Height != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - } - - break; - - case 4: - - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - switch (i) - { - case 0: - if (hv != 0x11 && hv != 0x22) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 1: - case 2: - if (hv != 0x11) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 3: - Size s0 = decoder.Components[0].SamplingFactors; - - if (s0.Width != h || s0.Height != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - break; - } - - this.SamplingFactors = new Size(h, v); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Block8x8 GetBlockReference(int column, int row) - { - return ref this.SpectralBlocks[column, row]; - } - - public void Dispose() - { - this.SpectralBlocks?.Dispose(); - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs deleted file mode 100644 index 6752768ff..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Represents a component scan - /// - [StructLayout(LayoutKind.Sequential)] - internal struct GolangComponentScan - { - /// - /// Gets or sets the component index. - /// - public byte ComponentIndex; - - /// - /// Gets or sets the DC table selector - /// - public byte DcTableSelector; - - /// - /// Gets or sets the AC table selector - /// - public byte AcTableSelector; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs deleted file mode 100644 index fa3364527..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Represents "recoverable" decoder errors. - /// - internal enum GolangDecoderErrorCode - { - /// - /// NoError - /// - NoError, - - /// - /// MissingFF00 - /// - // ReSharper disable once InconsistentNaming - MissingFF00, - - /// - /// End of stream reached unexpectedly - /// - UnexpectedEndOfStream - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs deleted file mode 100644 index dccce2aaa..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Represents a Huffman tree - /// - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct GolangHuffmanTree - { - /// - /// The index of the AC table row - /// - public const int AcTableIndex = 1; - - /// - /// The index of the DC table row - /// - public const int DcTableIndex = 0; - - /// - /// The maximum (inclusive) number of codes in a Huffman tree. - /// - public const int MaxNCodes = 256; - - /// - /// The maximum (inclusive) number of bits in a Huffman code. - /// - public const int MaxCodeLength = 16; - - /// - /// The maximum number of Huffman table classes - /// - public const int MaxTc = 1; - - /// - /// The maximum number of Huffman table identifiers - /// - public const int MaxTh = 3; - - /// - /// Row size of the Huffman table - /// - public const int ThRowSize = MaxTh + 1; - - /// - /// Number of Hufman Trees in the Huffman table - /// - public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1); - - /// - /// The log-2 size of the Huffman decoder's look-up table. - /// - public const int LutSizeLog2 = 8; - - /// - /// Gets or sets the number of codes in the tree. - /// - public int Length; - - /// - /// Gets the look-up table for the next LutSize bits in the bit-stream. - /// The high 8 bits of the uint16 are the encoded value. The low 8 bits - /// are 1 plus the code length, or 0 if the value is too large to fit in - /// lutSize bits. - /// - public FixedInt32Buffer256 Lut; - - /// - /// Gets the the decoded values, sorted by their encoding. - /// - public FixedInt32Buffer256 Values; - - /// - /// Gets the array of minimum codes. - /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. - /// - public FixedInt32Buffer16 MinCodes; - - /// - /// Gets the array of maximum codes. - /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length. - /// - public FixedInt32Buffer16 MaxCodes; - - /// - /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. - /// - public FixedInt32Buffer16 Indices; - - /// - /// Creates and initializes an array of instances of size - /// - /// An array of instances representing the Huffman tables - public static GolangHuffmanTree[] CreateHuffmanTrees() - { - return new GolangHuffmanTree[NumberOfTrees]; - } - - /// - /// Internal part of the DHT processor, whatever does it mean - /// - /// The decoder instance - /// The temporary buffer that holds the data that has been read from the Jpeg stream - /// Remaining bits - public void ProcessDefineHuffmanTablesMarkerLoop( - ref InputProcessor inputProcessor, - byte[] defineHuffmanTablesData, - ref int remaining) - { - // Read nCodes and huffman.Valuess (and derive h.Length). - // nCodes[i] is the number of codes with code length i. - // h.Length is the total number of codes. - this.Length = 0; - - int[] ncodes = new int[MaxCodeLength]; - for (int i = 0; i < ncodes.Length; i++) - { - ncodes[i] = defineHuffmanTablesData[i + 1]; - this.Length += ncodes[i]; - } - - if (this.Length == 0) - { - throw new ImageFormatException("Huffman table has zero length"); - } - - if (this.Length > MaxNCodes) - { - throw new ImageFormatException("Huffman table has excessive length"); - } - - remaining -= this.Length + 17; - if (remaining < 0) - { - throw new ImageFormatException("DHT has wrong length"); - } - - byte[] values = new byte[MaxNCodes]; - inputProcessor.ReadFull(values, 0, this.Length); - - fixed (int* valuesPtr = this.Values.Data) - fixed (int* lutPtr = this.Lut.Data) - { - for (int i = 0; i < values.Length; i++) - { - valuesPtr[i] = values[i]; - } - - // Derive the look-up table. - for (int i = 0; i < MaxNCodes; i++) - { - lutPtr[i] = 0; - } - - int x = 0, code = 0; - - for (int i = 0; i < LutSizeLog2; i++) - { - code <<= 1; - - for (int j = 0; j < ncodes[i]; j++) - { - // The codeLength is 1+i, so shift code by 8-(1+i) to - // calculate the high bits for every 8-bit sequence - // whose codeLength's high bits matches code. - // The high 8 bits of lutValue are the encoded value. - // The low 8 bits are 1 plus the codeLength. - int base2 = code << (7 - i); - int lutValue = (valuesPtr[x] << 8) | (2 + i); - - for (int k = 0; k < 1 << (7 - i); k++) - { - lutPtr[base2 | k] = lutValue; - } - - code++; - x++; - } - } - } - - fixed (int* minCodesPtr = this.MinCodes.Data) - fixed (int* maxCodesPtr = this.MaxCodes.Data) - fixed (int* indicesPtr = this.Indices.Data) - { - // Derive minCodes, maxCodes, and indices. - int c = 0, index = 0; - for (int i = 0; i < ncodes.Length; i++) - { - int nc = ncodes[i]; - if (nc == 0) - { - minCodesPtr[i] = -1; - maxCodesPtr[i] = -1; - indicesPtr[i] = -1; - } - else - { - minCodesPtr[i] = c; - maxCodesPtr[i] = c + nc - 1; - indicesPtr[i] = index; - c += nc; - index += nc; - } - - c <<= 1; - } - } - } - - /// - /// Gets the value for the given code and index. - /// - /// The code - /// The code length - /// The value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetValue(int code, int codeLength) - { - return this.Values[this.Indices[codeLength] + code - this.MinCodes[codeLength]]; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FixedInt32Buffer256 - { - public fixed int Data[256]; - - public int this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref int self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FixedInt32Buffer16 - { - public fixed int Data[16]; - - public int this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref int self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs deleted file mode 100644 index f3c8aa91b..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Formats.Jpeg.Components; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Conains the definition of - /// - internal unsafe partial struct GolangJpegScanDecoder - { - /// - /// Holds the "large" data blocks needed for computations. - /// - [StructLayout(LayoutKind.Sequential)] - public struct ComputationData - { - /// - /// The main input/working block - /// - public Block8x8 Block; - - /// - /// The jpeg unzig data - /// - public ZigZag Unzig; - - /// - /// The buffer storing the -s for each component - /// - public fixed byte ScanData[3 * GolangJpegDecoderCore.MaxComponents]; - - /// - /// The DC values for each component - /// - public fixed int Dc[GolangJpegDecoderCore.MaxComponents]; - - /// - /// Creates and initializes a new instance - /// - /// The - public static ComputationData Create() - { - ComputationData data = default; - data.Unzig = ZigZag.CreateUnzigTable(); - return data; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs deleted file mode 100644 index a00da6fca..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Formats.Jpeg.Components; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Conains the definition of - /// - internal unsafe partial struct GolangJpegScanDecoder - { - /// - /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of - /// - public struct DataPointers - { - /// - /// Pointer to - /// - public Block8x8* Block; - - /// - /// Pointer to as byte* - /// - public byte* Unzig; - - /// - /// Pointer to as Scan* - /// - public GolangComponentScan* ComponentScan; - - /// - /// Pointer to - /// - public int* Dc; - - /// - /// Initializes a new instance of the struct. - /// - /// The pointer pointing to - public DataPointers(ComputationData* basePtr) - { - this.Block = &basePtr->Block; - this.Unzig = basePtr->Unzig.Data; - this.ComponentScan = (GolangComponentScan*)basePtr->ScanData; - this.Dc = basePtr->Dc; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs deleted file mode 100644 index 3a88cfad4..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs +++ /dev/null @@ -1,705 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Formats.Jpeg.Components; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md! - /// - /// and are the spectral selection bounds. - /// and are the successive approximation high and low values. - /// The spec calls these values Ss, Se, Ah and Al. - /// For progressive JPEGs, these are the two more-or-less independent - /// aspects of progression. Spectral selection progression is when not - /// all of a block's 64 DCT coefficients are transmitted in one pass. - /// For example, three passes could transmit coefficient 0 (the DC - /// component), coefficients 1-5, and coefficients 6-63, in zig-zag - /// order. Successive approximation is when not all of the bits of a - /// band of coefficients are transmitted in one pass. For example, - /// three passes could transmit the 6 most significant bits, followed - /// by the second-least significant bit, followed by the least - /// significant bit. - /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. - /// - [StructLayout(LayoutKind.Sequential)] - internal unsafe partial struct GolangJpegScanDecoder - { - // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. -#pragma warning disable SA1202 // ElementsMustBeOrderedByAccess - - /// - /// The buffer - /// - private ComputationData data; - - /// - /// Pointers to elements of - /// - private DataPointers pointers; - - /// - /// The current component index - /// - public int ComponentIndex; - - /// - /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - private int bx; - - /// - /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - private int by; - - /// - /// Start index of the zig-zag selection bound - /// - private int zigStart; - - /// - /// End index of the zig-zag selection bound - /// - private int zigEnd; - - /// - /// Successive approximation high value - /// - private int ah; - - /// - /// Successive approximation low value - /// - private int al; - - /// - /// The number of component scans - /// - private int componentScanCount; - - /// - /// Horizontal sampling factor at the current component index - /// - private int hi; - - /// - /// End-of-Band run, specified in section G.1.2.2. - /// - private int eobRun; - - /// - /// The block counter - /// - private int blockCounter; - - /// - /// The MCU counter - /// - private int mcuCounter; - - /// - /// The expected RST marker value - /// - private byte expectedRst; - - /// - /// Initializes a default-constructed instance for reading data from -s stream. - /// - /// Pointer to on the stack - /// The instance - /// The remaining bytes in the segment block. - public static void InitStreamReading(GolangJpegScanDecoder* p, GolangJpegDecoderCore decoder, int remaining) - { - p->data = ComputationData.Create(); - p->pointers = new DataPointers(&p->data); - p->InitStreamReadingImpl(decoder, remaining); - } - - /// - /// Read Huffman data from Jpeg scans in , - /// and decode it as into . - /// - /// The blocks are traversed one MCU at a time. For 4:2:0 chroma - /// subsampling, there are four Y 8x8 blocks in every 16x16 MCU. - /// For a baseline 32x16 pixel image, the Y blocks visiting order is: - /// 0 1 4 5 - /// 2 3 6 7 - /// For progressive images, the interleaved scans (those with component count > 1) - /// are traversed as above, but non-interleaved scans are traversed left - /// to right, top to bottom: - /// 0 1 2 3 - /// 4 5 6 7 - /// Only DC scans (zigStart == 0) can be interleave AC scans must have - /// only one component. - /// To further complicate matters, for non-interleaved scans, there is no - /// data for any blocks that are inside the image at the MCU level but - /// outside the image at the pixel level. For example, a 24x16 pixel 4:2:0 - /// progressive image consists of two 16x16 MCUs. The interleaved scans - /// will process 8 Y blocks: - /// 0 1 4 5 - /// 2 3 6 7 - /// The non-interleaved scans will process only 6 Y blocks: - /// 0 1 2 - /// 3 4 5 - /// - /// The instance - public void DecodeBlocks(GolangJpegDecoderCore decoder) - { - decoder.InputProcessor.ResetErrorState(); - - this.blockCounter = 0; - this.mcuCounter = 0; - this.expectedRst = JpegConstants.Markers.RST0; - - for (int my = 0; my < decoder.MCUCountY; my++) - { - for (int mx = 0; mx < decoder.MCUCountX; mx++) - { - this.DecodeBlocksAtMcuIndex(decoder, mx, my); - - this.mcuCounter++; - - // Handling restart intervals - // Useful info: https://stackoverflow.com/a/8751802 - if (decoder.IsAtRestartInterval(this.mcuCounter)) - { - this.ProcessRSTMarker(decoder); - this.Reset(decoder); - } - } - } - } - - private void DecodeBlocksAtMcuIndex(GolangJpegDecoderCore decoder, int mx, int my) - { - for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) - { - this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - GolangComponent component = decoder.Components[this.ComponentIndex]; - - this.hi = component.HorizontalSamplingFactor; - int vi = component.VerticalSamplingFactor; - - for (int j = 0; j < this.hi * vi; j++) - { - if (this.componentScanCount != 1) - { - this.bx = (this.hi * mx) + (j % this.hi); - this.by = (vi * my) + (j / this.hi); - } - else - { - int q = decoder.MCUCountX * this.hi; - this.bx = this.blockCounter % q; - this.by = this.blockCounter / q; - this.blockCounter++; - if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) - { - continue; - } - } - - // Find the block at (bx,by) in the component's buffer: - ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); - - // Copy block to stack - this.data.Block = blockRefOnHeap; - - if (!decoder.InputProcessor.ReachedEOF) - { - this.DecodeBlock(decoder, scanIndex); - } - - // Store the result block: - blockRefOnHeap = this.data.Block; - } - } - } - - private void ProcessRSTMarker(GolangJpegDecoderCore decoder) - { - // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. - if (!decoder.InputProcessor.ReachedEOF) - { - decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); - if (decoder.InputProcessor.CheckEOFEnsureNoError()) - { - if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != this.expectedRst) - { - bool invalidRst = true; - - // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately - // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. - // If we identify that case we attempt to read until we have bypassed the padded bytes. - // We then check again for our RST marker and throw if invalid. - // No other methods are attempted to resynchronize from corrupt input. - if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) - { - while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) - { - decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - break; - } - } - - // Have we found a valid restart marker? - invalidRst = decoder.Temp[0] != this.expectedRst; - } - - if (invalidRst) - { - throw new ImageFormatException("Bad RST marker"); - } - } - - this.expectedRst++; - if (this.expectedRst == JpegConstants.Markers.RST7 + 1) - { - this.expectedRst = JpegConstants.Markers.RST0; - } - } - } - } - - private void Reset(GolangJpegDecoderCore decoder) - { - decoder.InputProcessor.ResetHuffmanDecoder(); - - this.ResetDcValues(); - - // Reset the progressive decoder state, as per section G.1.2.2. - this.eobRun = 0; - } - - /// - /// Reset the DC components, as per section F.2.1.3.1. - /// - private void ResetDcValues() - { - Unsafe.InitBlock(this.pointers.Dc, default, sizeof(int) * GolangJpegDecoderCore.MaxComponents); - } - - /// - /// The implementation part of as an instance method. - /// - /// The - /// The remaining bytes - private void InitStreamReadingImpl(GolangJpegDecoderCore decoder, int remaining) - { - if (decoder.ComponentCount == 0) - { - throw new ImageFormatException("Missing SOF marker"); - } - - if (remaining < 6 || 4 + (2 * decoder.ComponentCount) < remaining || remaining % 2 != 0) - { - throw new ImageFormatException("SOS has wrong length"); - } - - decoder.InputProcessor.ReadFull(decoder.Temp, 0, remaining); - this.componentScanCount = decoder.Temp[0]; - - int scanComponentCountX2 = 2 * this.componentScanCount; - if (remaining != 4 + scanComponentCountX2) - { - throw new ImageFormatException("SOS length inconsistent with number of components"); - } - - int totalHv = 0; - - for (int i = 0; i < this.componentScanCount; i++) - { - this.InitComponentScan(decoder, i, ref this.pointers.ComponentScan[i], ref totalHv); - } - - // Section B.2.3 states that if there is more than one component then the - // total H*V values in a scan must be <= 10. - if (decoder.ComponentCount > 1 && totalHv > 10) - { - throw new ImageFormatException("Total sampling factors too large."); - } - - this.zigEnd = Block8x8F.Size - 1; - - if (decoder.IsProgressive) - { - this.zigStart = decoder.Temp[1 + scanComponentCountX2]; - this.zigEnd = decoder.Temp[2 + scanComponentCountX2]; - this.ah = decoder.Temp[3 + scanComponentCountX2] >> 4; - this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f; - - if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd - || this.zigEnd >= Block8x8F.Size) - { - throw new ImageFormatException("Bad spectral selection bounds"); - } - - if (this.zigStart != 0 && this.componentScanCount != 1) - { - throw new ImageFormatException("Progressive AC coefficients for more than one component"); - } - - if (this.ah != 0 && this.ah != this.al + 1) - { - throw new ImageFormatException("Bad successive approximation values"); - } - } - } - - /// - /// Read the current the current block at (, ) from the decoders stream - /// - /// The decoder - /// The index of the scan - private void DecodeBlock(GolangJpegDecoderCore decoder, int scanIndex) - { - Block8x8* b = this.pointers.Block; - int huffmannIdx = (GolangHuffmanTree.AcTableIndex * GolangHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; - if (this.ah != 0) - { - this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); - } - else - { - int zig = this.zigStart; - - if (zig == 0) - { - zig++; - - // Decode the DC coefficient, as specified in section F.2.2.1. - int huffmanIndex = (GolangHuffmanTree.DcTableIndex * GolangHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; - decoder.InputProcessor.DecodeHuffmanUnsafe( - ref decoder.HuffmanTrees[huffmanIndex], - out int value); - if (!decoder.InputProcessor.CheckEOF()) - { - return; - } - - if (value > 16) - { - throw new ImageFormatException("Excessive DC component"); - } - - decoder.InputProcessor.ReceiveExtendUnsafe(value, out int deltaDC); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - return; - } - - this.pointers.Dc[this.ComponentIndex] += deltaDC; - - // b[0] = dc[compIndex] << al; - value = this.pointers.Dc[this.ComponentIndex] << this.al; - Block8x8.SetScalarAt(b, 0, (short)value); - } - - if (zig <= this.zigEnd && this.eobRun > 0) - { - this.eobRun--; - } - else - { - // Decode the AC coefficients, as specified in section F.2.2.2. - for (; zig <= this.zigEnd; zig++) - { - decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out int value); - if (decoder.InputProcessor.HasError) - { - return; - } - - int val0 = value >> 4; - int val1 = value & 0x0f; - if (val1 != 0) - { - zig += val0; - if (zig > this.zigEnd) - { - break; - } - - decoder.InputProcessor.ReceiveExtendUnsafe(val1, out int ac); - if (decoder.InputProcessor.HasError) - { - return; - } - - // b[Unzig[zig]] = ac << al; - value = ac << this.al; - Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)value); - } - else - { - if (val0 != 0x0f) - { - this.eobRun = (ushort)(1 << val0); - if (val0 != 0) - { - this.DecodeEobRun(val0, ref decoder.InputProcessor); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - return; - } - } - - this.eobRun--; - break; - } - - zig += 0x0f; - } - } - } - } - } - - private void DecodeEobRun(int count, ref InputProcessor processor) - { - processor.DecodeBitsUnsafe(count, out int bitsResult); - if (processor.LastErrorCode != GolangDecoderErrorCode.NoError) - { - return; - } - - this.eobRun |= bitsResult; - } - - private void InitComponentScan(GolangJpegDecoderCore decoder, int i, ref GolangComponentScan currentComponentScan, ref int totalHv) - { - // Component selector. - int cs = decoder.Temp[1 + (2 * i)]; - int compIndex = -1; - for (int j = 0; j < decoder.ComponentCount; j++) - { - // Component compv = ; - if (cs == decoder.Components[j].Identifier) - { - compIndex = j; - } - } - - if (compIndex < 0) - { - throw new ImageFormatException("Unknown component selector"); - } - - currentComponentScan.ComponentIndex = (byte)compIndex; - - this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, decoder.Components[compIndex]); - } - - private void ProcessComponentImpl( - GolangJpegDecoderCore decoder, - int i, - ref GolangComponentScan currentComponentScan, - ref int totalHv, - GolangComponent currentComponent) - { - // Section B.2.3 states that "the value of Cs_j shall be different from - // the values of Cs_1 through Cs_(j-1)". Since we have previously - // verified that a frame's component identifiers (C_i values in section - // B.2.2) are unique, it suffices to check that the implicit indexes - // into comp are unique. - for (int j = 0; j < i; j++) - { - if (currentComponentScan.ComponentIndex == this.pointers.ComponentScan[j].ComponentIndex) - { - throw new ImageFormatException("Repeated component selector"); - } - } - - totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor; - - currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); - if (currentComponentScan.DcTableSelector > GolangHuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad DC table selector value"); - } - - currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); - if (currentComponentScan.AcTableSelector > GolangHuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad AC table selector value"); - } - } - - /// - /// Decodes a successive approximation refinement block, as specified in section G.1.2. - /// - /// The instance - /// The Huffman tree - /// The low transform offset - private void Refine(ref InputProcessor bp, ref GolangHuffmanTree h, int delta) - { - Block8x8* b = this.pointers.Block; - - // Refining a DC component is trivial. - if (this.zigStart == 0) - { - if (this.zigEnd != 0) - { - throw new ImageFormatException("Invalid state for zig DC component"); - } - - bp.DecodeBitUnsafe(out bool bit); - if (!bp.CheckEOFEnsureNoError()) - { - return; - } - - if (bit) - { - int stuff = Block8x8.GetScalarAt(b, 0); - - // int stuff = (int)b[0]; - stuff |= delta; - - // b[0] = stuff; - Block8x8.SetScalarAt(b, 0, (short)stuff); - } - - return; - } - - // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. - int zig = this.zigStart; - if (this.eobRun == 0) - { - for (; zig <= this.zigEnd; zig++) - { - bool done = false; - int z = 0; - - bp.DecodeHuffmanUnsafe(ref h, out int val); - if (!bp.CheckEOF()) - { - return; - } - - int val0 = val >> 4; - int val1 = val & 0x0f; - - switch (val1) - { - case 0: - if (val0 != 0x0f) - { - this.eobRun = 1 << val0; - if (val0 != 0) - { - this.DecodeEobRun(val0, ref bp); - if (!bp.CheckEOFEnsureNoError()) - { - return; - } - } - - done = true; - } - - break; - case 1: - z = delta; - - bp.DecodeBitUnsafe(out bool bit); - if (!bp.CheckEOFEnsureNoError()) - { - return; - } - - if (!bit) - { - z = -z; - } - - break; - default: - throw new ImageFormatException("Unexpected Huffman code"); - } - - if (done) - { - break; - } - - zig = this.RefineNonZeroes(ref bp, zig, val0, delta); - - if (bp.ReachedEOF || bp.HasError) - { - return; - } - - if (z != 0 && zig <= this.zigEnd) - { - // b[Unzig[zig]] = z; - Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)z); - } - } - } - - if (this.eobRun > 0) - { - this.eobRun--; - this.RefineNonZeroes(ref bp, zig, -1, delta); - } - } - - /// - /// Refines non-zero entries of b in zig-zag order. - /// If >= 0, the first zero entries are skipped over. - /// - /// The - /// The zig-zag start index - /// The non-zero entry - /// The low transform offset - /// The - private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta) - { - Block8x8* b = this.pointers.Block; - for (; zig <= this.zigEnd; zig++) - { - int u = this.pointers.Unzig[zig]; - int bu = Block8x8.GetScalarAt(b, u); - - // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? - if (bu == 0) - { - if (nz == 0) - { - break; - } - - nz--; - continue; - } - - bp.DecodeBitUnsafe(out bool bit); - if (bp.HasError) - { - return int.MinValue; - } - - if (!bit) - { - continue; - } - - int val = bu >= 0 ? bu + delta : bu - delta; - - Block8x8.SetScalarAt(b, u, (short)val); - } - - return zig; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs deleted file mode 100644 index c7e14ee4f..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Encapsulates stream reading and processing data and operations for . - /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s - /// - internal struct InputProcessor : IDisposable - { - /// - /// Holds the unprocessed bits that have been taken from the byte-stream. - /// - public Bits Bits; - - /// - /// The byte buffer - /// - public Bytes Bytes; - - /// - /// Initializes a new instance of the struct. - /// - /// The input - /// Temporal buffer, same as - public InputProcessor(Stream inputStream, byte[] temp) - { - this.Bits = default; - this.Bytes = Bytes.Create(); - this.InputStream = inputStream; - this.Temp = temp; - this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Gets the input stream - /// - public Stream InputStream { get; } - - /// - /// Gets the temporary buffer, same instance as - /// - public byte[] Temp { get; } - - /// - /// Gets a value indicating whether an unexpected EOF reached in . - /// - public bool ReachedEOF => this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream; - - public bool HasError => this.LastErrorCode != GolangDecoderErrorCode.NoError; - - public GolangDecoderErrorCode LastErrorCode { get; private set; } - - public void ResetErrorState() => this.LastErrorCode = GolangDecoderErrorCode.NoError; - - /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. - /// Calls and returns true otherwise. - /// - /// A indicating whether EOF reached - public bool CheckEOFEnsureNoError() - { - if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream) - { - return false; - } - - this.LastErrorCode.EnsureNoError(); - return true; - } - - /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. - /// Returns true otherwise. - /// - /// A indicating whether EOF reached - public bool CheckEOF() - { - if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream) - { - return false; - } - - return true; - } - - /// - public void Dispose() - { - this.Bytes.Dispose(); - } - - /// - /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte() - { - return this.Bytes.ReadByte(this.InputStream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GolangDecoderErrorCode ReadByteUnsafe(out byte result) - { - this.LastErrorCode = this.Bytes.ReadByteUnsafe(this.InputStream, out result); - return this.LastErrorCode; - } - - /// - /// Decodes a single bit - /// TODO: This method (and also the usages) could be optimized by batching! - /// - /// The decoded bit as a - /// The - public GolangDecoderErrorCode DecodeBitUnsafe(out bool result) - { - if (this.Bits.UnreadBits == 0) - { - this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this); - if (this.LastErrorCode != GolangDecoderErrorCode.NoError) - { - result = false; - return this.LastErrorCode; - } - } - - result = (this.Bits.Accumulator & this.Bits.Mask) != 0; - this.Bits.UnreadBits--; - this.Bits.Mask >>= 1; - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Reads exactly length bytes into data. It does not care about byte stuffing. - /// Does not throw on errors, returns instead! - /// - /// The data to write to. - /// The offset in the source buffer - /// The number of bytes to read - /// The - public GolangDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) - { - // Unread the overshot bytes, if any. - if (this.Bytes.UnreadableBytes != 0) - { - if (this.Bits.UnreadBits >= 8) - { - this.UnreadByteStuffedByte(); - } - - this.Bytes.UnreadableBytes = 0; - } - - this.LastErrorCode = GolangDecoderErrorCode.NoError; - while (length > 0 && this.LastErrorCode == GolangDecoderErrorCode.NoError) - { - if (this.Bytes.J - this.Bytes.I >= length) - { - Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length); - this.Bytes.I += length; - length -= length; - } - else - { - Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I); - offset += this.Bytes.J - this.Bytes.I; - length -= this.Bytes.J - this.Bytes.I; - this.Bytes.I += this.Bytes.J - this.Bytes.I; - - this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); - } - } - - return this.LastErrorCode; - } - - /// - /// Decodes the given number of bits - /// - /// The number of bits to decode. - /// The result - /// The - public GolangDecoderErrorCode DecodeBitsUnsafe(int count, out int result) - { - if (this.Bits.UnreadBits < count) - { - this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this); - if (this.LastErrorCode != GolangDecoderErrorCode.NoError) - { - result = 0; - return this.LastErrorCode; - } - } - - result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); - result = result & ((1 << count) - 1); - this.Bits.UnreadBits -= count; - this.Bits.Mask >>= count; - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Extracts the next Huffman-coded value from the bit-stream into result, decoded according to the given value. - /// - /// The huffman value - /// The decoded - /// The - public GolangDecoderErrorCode DecodeHuffmanUnsafe(ref GolangHuffmanTree huffmanTree, out int result) - { - result = 0; - - if (huffmanTree.Length == 0) - { - DecoderThrowHelper.ThrowImageFormatException.UninitializedHuffmanTable(); - } - - if (this.Bits.UnreadBits < 8) - { - this.LastErrorCode = this.Bits.Ensure8BitsUnsafe(ref this); - - if (this.LastErrorCode == GolangDecoderErrorCode.NoError) - { - int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - GolangHuffmanTree.LutSizeLog2)) & 0xFF; - int v = huffmanTree.Lut[lutIndex]; - - if (v != 0) - { - int n = (v & 0xFF) - 1; - this.Bits.UnreadBits -= n; - this.Bits.Mask >>= n; - result = v >> 8; - return this.LastErrorCode; - } - } - else - { - this.UnreadByteStuffedByte(); - return this.LastErrorCode; - } - } - - int code = 0; - for (int i = 0; i < GolangHuffmanTree.MaxCodeLength; i++) - { - if (this.Bits.UnreadBits == 0) - { - this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(1, ref this); - - if (this.HasError) - { - return this.LastErrorCode; - } - } - - if ((this.Bits.Accumulator & this.Bits.Mask) != 0) - { - code |= 1; - } - - this.Bits.UnreadBits--; - this.Bits.Mask >>= 1; - - if (code <= huffmanTree.MaxCodes[i]) - { - result = huffmanTree.GetValue(code, i); - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - code <<= 1; - } - - // Unrecoverable error, throwing: - DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode(); - - // DUMMY RETURN! C# doesn't know we have thrown an exception! - return GolangDecoderErrorCode.NoError; - } - - /// - /// Skips the next n bytes. - /// - /// The number of bytes to ignore. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Skip(int count) - { - this.LastErrorCode = this.SkipUnsafe(count); - this.LastErrorCode.EnsureNoError(); - } - - /// - /// Skips the next n bytes. - /// Does not throw, returns instead! - /// - /// The number of bytes to ignore. - /// The - public GolangDecoderErrorCode SkipUnsafe(int count) - { - // Unread the overshot bytes, if any. - if (this.Bytes.UnreadableBytes != 0) - { - if (this.Bits.UnreadBits >= 8) - { - this.UnreadByteStuffedByte(); - } - - this.Bytes.UnreadableBytes = 0; - } - - while (true) - { - int m = this.Bytes.J - this.Bytes.I; - if (m > count) - { - m = count; - } - - this.Bytes.I += m; - count -= m; - if (count == 0) - { - break; - } - - this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); - if (this.LastErrorCode != GolangDecoderErrorCode.NoError) - { - return this.LastErrorCode; - } - } - - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Reads exactly length bytes into data. It does not care about byte stuffing. - /// - /// The data to write to. - /// The offset in the source buffer - /// The number of bytes to read - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ReadFull(byte[] data, int offset, int length) - { - this.LastErrorCode = this.ReadFullUnsafe(data, offset, length); - this.LastErrorCode.EnsureNoError(); - } - - /// - /// Undoes the most recent ReadByteStuffedByte call, - /// giving a byte of data back from bits to bytes. The Huffman look-up table - /// requires at least 8 bits for look-up, which means that Huffman decoding can - /// sometimes overshoot and read one or two too many bytes. Two-byte overshoot - /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte. - /// - public void UnreadByteStuffedByte() - { - this.Bytes.I -= this.Bytes.UnreadableBytes; - this.Bytes.UnreadableBytes = 0; - if (this.Bits.UnreadBits >= 8) - { - this.Bits.Accumulator >>= 8; - this.Bits.UnreadBits -= 8; - this.Bits.Mask >>= 8; - } - } - - /// - /// Receive extend - /// - /// Byte - /// Read bits value - /// The - public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, out int x) - { - this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); - return this.LastErrorCode; - } - - /// - /// Reset the Huffman decoder. - /// - public void ResetHuffmanDecoder() - { - this.Bits = default; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md deleted file mode 100644 index 4ca4d1f64..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md +++ /dev/null @@ -1,25 +0,0 @@ -## JpegScanDecoder -Encapsulates the impementation of the Jpeg top-to bottom scan decoder triggered by the `SOS` marker. -The implementation is optimized to hold most of the necessary data in a single value type, which is intended to be used as an on-stack object. - -#### Benefits: -- Maximized locality of reference by keeping most of the operation data on the stack -- Achieving this without long parameter lists, most of the values describing the state of the decoder algorithm -are members of the `JpegScanDecoder` struct -- Most of the logic related to Scan decoding is refactored & simplified now to live in the methods of `JpegScanDecoder` -- The first step is done towards separating the stream reading from block processing. They can be refactored later to be executed in two disctinct loops. - - The input processing loop can be `async` - - The block processing loop can be parallelized - -#### Data layout - -|JpegScanDecoder | -|-------------------| -|Variables | -|DataPointers | -|ComputationData | - -- **ComputationData** holds the "large" data blocks needed for computations (Mostly `Block8x8F`-s) -- **DataPointers** contains pointers to the memory regions of `ComponentData` so they can be easily passed around to pointer based utility methods of `Block8x8F` - - diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs deleted file mode 100644 index 005034b9d..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// The missing ff00 exception. - /// - // ReSharper disable once InconsistentNaming - internal class MissingFF00Exception : Exception - { - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs deleted file mode 100644 index 29255204b..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort -{ - /// - /// Image decoder for generating an image out of a jpg stream. - /// - internal sealed class GolangJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector - { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new GolangJpegDecoderCore(configuration, this)) - { - return decoder.Decode(stream); - } - } - - /// - public IImageInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new GolangJpegDecoderCore(configuration, this)) - { - return decoder.Identify(stream); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs deleted file mode 100644 index 46cdcddb4..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs +++ /dev/null @@ -1,824 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using System.IO; - -using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort -{ - /// - /// - /// Performs the jpeg decoding operation. - /// - internal sealed unsafe class GolangJpegDecoderCore : IRawJpegData - { - /// - /// The maximum number of color components - /// - public const int MaxComponents = 4; - - /// - /// The maximum number of quantization tables - /// - public const int MaxTq = 3; - - /// - /// The only supported precision - /// - public const int SupportedPrecision = 8; - - // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P -#pragma warning disable SA1401 // FieldsMustBePrivate - - /// - /// Encapsulates stream reading and processing data and operations for . - /// It's a value type for improved data locality, and reduced number of CALLVIRT-s - /// - public InputProcessor InputProcessor; -#pragma warning restore SA401 - - /// - /// The global configuration - /// - private readonly Configuration configuration; - - /// - /// Whether the image has a JFIF header - /// It's faster to check this than to use the equality operator on the struct - /// - private bool isJFif; - - /// - /// Contains information about the JFIF marker - /// - private JFifMarker jFif; - - /// - /// Whether the image has a EXIF header - /// - private bool isExif; - - /// - /// Whether the image has an Adobe marker. - /// It's faster to check this than to use the equality operator on the struct - /// - private bool isAdobe; - - /// - /// Contains information about the Adobe marker - /// - private AdobeMarker adobe; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public GolangJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) - { - this.IgnoreMetadata = options.IgnoreMetadata; - this.configuration = configuration ?? Configuration.Default; - this.Temp = new byte[2 * Block8x8F.Size]; - } - - /// - public JpegColorSpace ColorSpace { get; private set; } - - /// - /// Gets the component array - /// - public GolangComponent[] Components { get; private set; } - - /// - /// Gets the huffman trees - /// - public GolangHuffmanTree[] HuffmanTrees { get; private set; } - - /// - public Block8x8F[] QuantizationTables { get; private set; } - - /// - /// Gets the temporary buffer used to store bytes read from the stream. - /// TODO: Should be stack allocated, fixed sized buffer! - /// - public byte[] Temp { get; } - - /// - public Size ImageSizeInPixels { get; private set; } - - /// - /// Gets the number of MCU blocks in the image as . - /// - public Size ImageSizeInMCU { get; private set; } - - /// - public int ComponentCount { get; private set; } - - IEnumerable IRawJpegData.Components => this.Components; - - /// - /// Gets the color depth, in number of bits per pixel. - /// - public int BitsPerPixel => this.ComponentCount * SupportedPrecision; - - /// - /// Gets the image height - /// - public int ImageHeight => this.ImageSizeInPixels.Height; - - /// - /// Gets the image width - /// - public int ImageWidth => this.ImageSizeInPixels.Width; - - /// - /// Gets the input stream. - /// - public Stream InputStream { get; private set; } - - /// - /// Gets a value indicating whether the image is interlaced (progressive) - /// - public bool IsProgressive { get; private set; } - - /// - /// Gets the restart interval - /// - public int RestartInterval { get; private set; } - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the X axis - /// - public int MCUCountX => this.ImageSizeInMCU.Width; - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the Y axis - /// - public int MCUCountY => this.ImageSizeInMCU.Height; - - /// - /// Gets the total number of MCU-s (Minimum Coded Units) in the image. - /// - public int TotalMCUCount => this.MCUCountX * this.MCUCountY; - - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; } - - /// - /// Gets the decoded by this decoder instance. - /// - public ImageMetaData MetaData { get; private set; } - - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. - public Image Decode(Stream stream) - where TPixel : struct, IPixel - { - this.ParseStream(stream); - return this.PostProcessIntoImage(); - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public IImageInfo Identify(Stream stream) - { - this.ParseStream(stream, true); - return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); - } - - /// - public void Dispose() - { - if (this.Components != null) - { - foreach (GolangComponent component in this.Components) - { - component?.Dispose(); - } - } - - this.InputProcessor.Dispose(); - } - - /// - /// Read metadata from stream and read the blocks in the scans into . - /// - /// The stream - /// Whether to decode metadata only. - public void ParseStream(Stream stream, bool metadataOnly = false) - { - this.MetaData = new ImageMetaData(); - this.InputStream = stream; - this.InputProcessor = new InputProcessor(stream, this.Temp); - - if (!metadataOnly) - { - this.HuffmanTrees = GolangHuffmanTree.CreateHuffmanTrees(); - this.QuantizationTables = new Block8x8F[MaxTq + 1]; - } - - // Check for the Start Of Image marker. - this.InputProcessor.ReadFull(this.Temp, 0, 2); - - if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI) - { - throw new ImageFormatException("Missing SOI marker."); - } - - // Process the remaining segments until the End Of Image marker. - bool processBytes = true; - - // we can't currently short circute progressive images so don't try. - while (processBytes) - { - this.InputProcessor.ReadFull(this.Temp, 0, 2); - - if (this.InputProcessor.ReachedEOF) - { - // We've reached the end of the stream. - processBytes = false; - } - - while (this.Temp[0] != 0xff) - { - // Strictly speaking, this is a format error. However, libjpeg is - // liberal in what it accepts. As of version 9, next_marker in - // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and - // continues to decode the stream. Even before next_marker sees - // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many - // bytes as it can, possibly past the end of a scan's data. It - // effectively puts back any markers that it overscanned (e.g. an - // "\xff\xd9" EOI marker), but it does not put back non-marker data, - // and thus it can silently ignore a small number of extraneous - // non-marker bytes before next_marker has a chance to see them (and - // print a warning). - // We are therefore also liberal in what we accept. Extraneous data - // is silently ignore - // This is similar to, but not exactly the same as, the restart - // mechanism within a scan (the RST[0-7] markers). - // Note that extraneous 0xff bytes in e.g. SOS data are escaped as - // "\xff\x00", and so are detected a little further down below. - this.Temp[0] = this.Temp[1]; - this.Temp[1] = this.InputProcessor.ReadByte(); - } - - byte marker = this.Temp[1]; - if (marker == 0) - { - // Treat "\xff\x00" as extraneous data. - continue; - } - - while (marker == 0xff) - { - // Section B.1.1.2 says, "Any marker may optionally be preceded by any - // number of fill bytes, which are bytes assigned code X'FF'". - this.InputProcessor.ReadByteUnsafe(out marker); - - if (this.InputProcessor.ReachedEOF) - { - // We've reached the end of the stream. - processBytes = false; - break; - } - } - - // End Of Image. - if (marker == JpegConstants.Markers.EOI) - { - break; - } - - if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7) - { - // Figures B.2 and B.16 of the specification suggest that restart markers should - // only occur between Entropy Coded Segments and not after the final ECS. - // However, some encoders may generate incorrect JPEGs with a final restart - // marker. That restart marker will be seen here instead of inside the ProcessSOS - // method, and is ignored as a harmless error. Restart markers have no extra data, - // so we check for this before we read the 16-bit length of the segment. - continue; - } - - // Read the 16-bit length of the segment. The value includes the 2 bytes for the - // length itself, so we subtract 2 to get the number of remaining bytes. - this.InputProcessor.ReadFullUnsafe(this.Temp, 0, 2); - int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; - if (remaining < 0) - { - throw new ImageFormatException("Short segment length."); - } - - switch (marker) - { - case JpegConstants.Markers.SOF0: - case JpegConstants.Markers.SOF1: - case JpegConstants.Markers.SOF2: - this.IsProgressive = marker == JpegConstants.Markers.SOF2; - this.ProcessStartOfFrameMarker(remaining, metadataOnly); - - break; - case JpegConstants.Markers.DHT: - - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineHuffmanTablesMarker(remaining); - } - - break; - case JpegConstants.Markers.DQT: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineQuantizationTablesMarker(remaining); - } - - break; - case JpegConstants.Markers.SOS: - if (!metadataOnly) - { - this.ProcessStartOfScanMarker(remaining); - if (this.InputProcessor.ReachedEOF) - { - // If unexpected EOF reached. We can stop processing bytes as we now have the image data. - processBytes = false; - } - } - else - { - // It's highly unlikely that APPn related data will be found after the SOS marker - // We should have gathered everything we need by now. - processBytes = false; - } - - break; - - case JpegConstants.Markers.DRI: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineRestartIntervalMarker(remaining); - } - - break; - case JpegConstants.Markers.APP0: - this.ProcessApplicationHeaderMarker(remaining); - break; - case JpegConstants.Markers.APP1: - this.ProcessApp1Marker(remaining); - break; - case JpegConstants.Markers.APP2: - this.ProcessApp2Marker(remaining); - break; - case JpegConstants.Markers.APP14: - this.ProcessApp14Marker(remaining); - break; - default: - if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15) - || marker == JpegConstants.Markers.COM) - { - this.InputProcessor.Skip(remaining); - } - - break; - } - } - - this.InitDerivedMetaDataProperties(); - } - - /// - /// Returns true if 'mcuCounter' is at restart interval - /// - public bool IsAtRestartInterval(int mcuCounter) - { - return this.RestartInterval > 0 && mcuCounter % this.RestartInterval == 0 - && mcuCounter < this.TotalMCUCount; - } - - /// - /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. - /// - private void InitDerivedMetaDataProperties() - { - if (this.isJFif) - { - this.MetaData.HorizontalResolution = this.jFif.XDensity; - this.MetaData.VerticalResolution = this.jFif.YDensity; - } - else if (this.isExif) - { - double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizonalTag) - ? ((Rational)horizonalTag.Value).ToDouble() - : 0; - - double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag) - ? ((Rational)verticalTag.Value).ToDouble() - : 0; - - if (horizontalValue > 0 && verticalValue > 0) - { - this.MetaData.HorizontalResolution = horizontalValue; - this.MetaData.VerticalResolution = verticalValue; - } - } - - if (this.MetaData.IccProfile?.CheckIsValid() == false) - { - this.MetaData.IccProfile = null; - } - } - - /// - /// Processes the application header containing the JFIF identifier plus extra data. - /// - /// The remaining bytes in the segment block. - private void ProcessApplicationHeaderMarker(int remaining) - { - if (remaining < 5) - { - this.InputProcessor.Skip(remaining); - return; - } - - const int MarkerLength = JFifMarker.Length; - this.InputProcessor.ReadFull(this.Temp, 0, MarkerLength); - remaining -= MarkerLength; - - this.isJFif = JFifMarker.TryParse(this.Temp, out this.jFif); - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the App1 marker retrieving any stored metadata - /// - /// The remaining bytes in the segment block. - private void ProcessApp1Marker(int remaining) - { - if (remaining < 6 || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (ProfileResolver.IsProfile(profile, ProfileResolver.ExifMarker)) - { - this.isExif = true; - this.MetaData.ExifProfile = new ExifProfile(profile); - } - } - - /// - /// Processes the App2 marker retrieving any stored ICC profile information - /// - /// The remaining bytes in the segment block. - private void ProcessApp2Marker(int remaining) - { - // Length is 14 though we only need to check 12. - const int Icclength = 14; - if (remaining < Icclength || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] identifier = new byte[Icclength]; - this.InputProcessor.ReadFull(identifier, 0, Icclength); - remaining -= Icclength; // We have read it by this point - - if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker)) - { - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (this.MetaData.IccProfile == null) - { - this.MetaData.IccProfile = new IccProfile(profile); - } - else - { - this.MetaData.IccProfile.Extend(profile); - } - } - else - { - // Not an ICC profile we can handle. Skip the remaining bytes so we can carry on and ignore this. - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the application header containing the Adobe identifier - /// which stores image encoding information for DCT filters. - /// - /// The remaining bytes in the segment block. - private void ProcessApp14Marker(int remaining) - { - const int MarkerLength = AdobeMarker.Length; - if (remaining < MarkerLength) - { - // Skip the application header length - this.InputProcessor.Skip(remaining); - return; - } - - this.InputProcessor.ReadFull(this.Temp, 0, MarkerLength); - remaining -= MarkerLength; - - this.isAdobe = AdobeMarker.TryParse(this.Temp, out this.adobe); - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. - /// - /// The remaining bytes in the segment block. - /// - /// Thrown if the tables do not match the header - /// - private void ProcessDefineQuantizationTablesMarker(int remaining) - { - while (remaining > 0) - { - bool done = false; - - remaining--; - byte x = this.InputProcessor.ReadByte(); - int tq = x & 0x0F; - if (tq > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - switch (x >> 4) - { - case 0: - if (remaining < Block8x8F.Size) - { - done = true; - break; - } - - remaining -= Block8x8F.Size; - this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.Size); - - for (int i = 0; i < Block8x8F.Size; i++) - { - this.QuantizationTables[tq][i] = this.Temp[i]; - } - - break; - case 1: - if (remaining < 2 * Block8x8F.Size) - { - done = true; - break; - } - - remaining -= 2 * Block8x8F.Size; - this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.Size); - - for (int i = 0; i < Block8x8F.Size; i++) - { - this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; - } - - break; - default: - throw new ImageFormatException("Bad Pq value"); - } - - if (done) - { - break; - } - } - - if (remaining != 0) - { - throw new ImageFormatException("DQT has wrong length"); - } - } - - /// - /// Processes the Start of Frame marker. Specified in section B.2.2. - /// - /// The remaining bytes in the segment block. - /// Whether to decode metadata only. - private void ProcessStartOfFrameMarker(int remaining, bool metadataOnly) - { - if (this.ComponentCount != 0) - { - throw new ImageFormatException("Multiple SOF markers"); - } - - switch (remaining) - { - case 6 + (3 * 1): // grayscale image. - this.ComponentCount = 1; - break; - case 6 + (3 * 3): // YCbCr or RGB image. - this.ComponentCount = 3; - break; - case 6 + (3 * 4): // YCbCrK or CMYK image. - this.ComponentCount = 4; - break; - default: - throw new ImageFormatException("Incorrect number of components"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - - // We only support 8-bit precision. - if (this.Temp[0] != SupportedPrecision) - { - throw new ImageFormatException("Only 8-Bit precision supported."); - } - - int height = (this.Temp[1] << 8) + this.Temp[2]; - int width = (this.Temp[3] << 8) + this.Temp[4]; - - this.ImageSizeInPixels = new Size(width, height); - - if (this.Temp[5] != this.ComponentCount) - { - throw new ImageFormatException("SOF has wrong length"); - } - - if (!metadataOnly) - { - this.Components = new GolangComponent[this.ComponentCount]; - - for (int i = 0; i < this.ComponentCount; i++) - { - byte componentIdentifier = this.Temp[6 + (3 * i)]; - var component = new GolangComponent(componentIdentifier, i); - component.InitializeCoreData(this); - this.Components[i] = component; - } - - int h0 = this.Components[0].HorizontalSamplingFactor; - int v0 = this.Components[0].VerticalSamplingFactor; - - this.ImageSizeInMCU = this.ImageSizeInPixels.DivideRoundUp(8 * h0, 8 * v0); - - this.ColorSpace = this.DeduceJpegColorSpace(); - - foreach (GolangComponent component in this.Components) - { - component.InitializeDerivedData(this.configuration.MemoryAllocator, this); - } - } - } - - /// - /// Processes a Define Huffman Table marker, and initializes a huffman - /// struct from its contents. Specified in section B.2.4.2. - /// - /// The remaining bytes in the segment block. - private void ProcessDefineHuffmanTablesMarker(int remaining) - { - while (remaining > 0) - { - if (remaining < 17) - { - throw new ImageFormatException($"DHT has wrong length. {remaining}"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, 17); - - int tc = this.Temp[0] >> 4; - if (tc > GolangHuffmanTree.MaxTc) - { - throw new ImageFormatException("Bad Tc value"); - } - - int th = this.Temp[0] & 0x0f; - if (th > GolangHuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad Th value"); - } - - int huffTreeIndex = (tc * GolangHuffmanTree.ThRowSize) + th; - this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( - ref this.InputProcessor, - this.Temp, - ref remaining); - } - } - - /// - /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in - /// macroblocks - /// - /// The remaining bytes in the segment block. - private void ProcessDefineRestartIntervalMarker(int remaining) - { - if (remaining != 2) - { - throw new ImageFormatException("DRI has wrong length"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; - } - - /// - /// Processes the SOS (Start of scan marker). - /// - /// The remaining bytes in the segment block. - /// - /// Missing SOF Marker - /// SOS has wrong length - /// - private void ProcessStartOfScanMarker(int remaining) - { - GolangJpegScanDecoder scan = default; - GolangJpegScanDecoder.InitStreamReading(&scan, this, remaining); - this.InputProcessor.Bits = default; - scan.DecodeBlocks(this); - } - - private JpegColorSpace DeduceJpegColorSpace() - { - switch (this.ComponentCount) - { - case 1: - return JpegColorSpace.Grayscale; - case 3: - if (!this.isAdobe || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) - { - return JpegColorSpace.YCbCr; - } - - if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) - { - return JpegColorSpace.RGB; - } - - break; - case 4: - if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck) - { - return JpegColorSpace.Ycck; - } - - return JpegColorSpace.Cmyk; - } - - throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}." - + "JpegDecoder only supports YCbCr, RGB, YccK, CMYK and grayscale color spaces."); - } - - private Image PostProcessIntoImage() - where TPixel : struct, IPixel - { - using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this)) - { - var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); - postProcessor.PostProcess(image.Frames.RootFrame); - return image; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index eafbb391c..57b70dd26 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.IO; - -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg @@ -24,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { Guard.NotNull(stream, nameof(stream)); - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) + using (var decoder = new JpegDecoderCore(configuration, this)) { return decoder.Decode(stream); } @@ -35,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { Guard.NotNull(stream, nameof(stream)); - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) + using (var decoder = new JpegDecoderCore(configuration, this)) { return decoder.Identify(stream); } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs rename to src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 69b5e93b6..5c4dbfc24 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -10,7 +10,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; @@ -19,14 +18,14 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +namespace SixLabors.ImageSharp.Formats.Jpeg { /// /// Performs the jpeg decoding operation. /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// - internal sealed class PdfJsJpegDecoderCore : IRawJpegData + internal sealed class JpegDecoderCore : IRawJpegData { /// /// The only supported precision @@ -51,12 +50,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// The DC HUffman tables /// - private PdfJsHuffmanTables dcHuffmanTables; + private HuffmanTables dcHuffmanTables; /// /// The AC HUffman tables /// - private PdfJsHuffmanTables acHuffmanTables; + private HuffmanTables acHuffmanTables; /// /// The fast AC tables used for entropy decoding @@ -84,11 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort private AdobeMarker adobe; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The configuration. /// The options. - public PdfJsJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) + public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) { this.configuration = configuration ?? Configuration.Default; this.IgnoreMetadata = options.IgnoreMetadata; @@ -97,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// Gets the frame /// - public PdfJsFrame Frame { get; private set; } + public JpegFrame Frame { get; private set; } /// public Size ImageSizeInPixels { get; private set; } @@ -146,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// Gets the components. /// - public PdfJsFrameComponent[] Components => this.Frame.Components; + public JpegFrameComponent[] Components => this.Frame.Components; /// IEnumerable IRawJpegData.Components => this.Components; @@ -159,14 +158,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// The buffer to read file markers to /// The input stream - /// The - public static PdfJsFileMarker FindNextFileMarker(byte[] marker, DoubleBufferedStreamReader stream) + /// The + public static JpegFileMarker FindNextFileMarker(byte[] marker, DoubleBufferedStreamReader stream) { int value = stream.Read(marker, 0, 2); if (value == 0) { - return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); } if (marker[0] == JpegConstants.Markers.XFF) @@ -179,16 +178,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort int suffix = stream.ReadByte(); if (suffix == -1) { - return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); } m = suffix; } - return new PdfJsFileMarker((byte)m, stream.Position - 2); + return new JpegFileMarker((byte)m, stream.Position - 2); } - return new PdfJsFileMarker(marker[1], stream.Position - 2, true); + return new JpegFileMarker(marker[1], stream.Position - 2, true); } /// @@ -228,7 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort // Check for the Start Of Image marker. this.InputStream.Read(this.markerBuffer, 0, 2); - var fileMarker = new PdfJsFileMarker(this.markerBuffer[1], 0); + var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0); if (fileMarker.Marker != JpegConstants.Markers.SOI) { throw new ImageFormatException("Missing SOI marker."); @@ -236,14 +235,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort this.InputStream.Read(this.markerBuffer, 0, 2); byte marker = this.markerBuffer[1]; - fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2); + fileMarker = new JpegFileMarker(marker, (int)this.InputStream.Position - 2); // Only assign what we need if (!metadataOnly) { this.QuantizationTables = new Block8x8F[4]; - this.dcHuffmanTables = new PdfJsHuffmanTables(); - this.acHuffmanTables = new PdfJsHuffmanTables(); + this.dcHuffmanTables = new HuffmanTables(); + this.acHuffmanTables = new HuffmanTables(); this.fastACTables = new FastACTables(this.configuration.MemoryAllocator); } @@ -630,7 +629,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// The remaining bytes in the segment block. /// The current frame marker. /// Whether to parse metadata only - private void ProcessStartOfFrameMarker(int remaining, PdfJsFileMarker frameMarker, bool metadataOnly) + private void ProcessStartOfFrameMarker(int remaining, JpegFileMarker frameMarker, bool metadataOnly) { if (this.Frame != null) { @@ -645,7 +644,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException("Only 8-Bit precision supported."); } - this.Frame = new PdfJsFrame + this.Frame = new JpegFrame { Extended = frameMarker.Marker == JpegConstants.Markers.SOF1, Progressive = frameMarker.Marker == JpegConstants.Markers.SOF2, @@ -667,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort { // No need to pool this. They max out at 4 this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; - this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount]; + this.Frame.Components = new JpegFrameComponent[this.Frame.ComponentCount]; this.ColorSpace = this.DeduceJpegColorSpace(); for (int i = 0; i < this.Frame.ComponentCount; i++) @@ -686,7 +685,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort maxV = v; } - var component = new PdfJsFrameComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + var component = new JpegFrameComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); this.Frame.Components[i] = component; this.Frame.ComponentIds[i] = component.Id; @@ -794,7 +793,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException("Unknown component selector"); } - ref PdfJsFrameComponent component = ref this.Frame.Components[componentIndex]; + ref JpegFrameComponent component = ref this.Frame.Components[componentIndex]; int tableSpec = this.InputStream.ReadByte(); component.DCHuffmanTableId = tableSpec >> 4; component.ACHuffmanTableId = tableSpec & 15; @@ -831,9 +830,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// The codelengths /// The values [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) + private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) { - tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); + tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); } /// diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs deleted file mode 100644 index e12278cc7..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort -{ - /// - /// Image decoder for generating an image out of a jpg stream. - /// - internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector - { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) - { - return decoder.Decode(stream); - } - } - - /// - public IImageInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) - { - return decoder.Identify(stream); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index f86919dd1..9b968e4db 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -4,8 +4,8 @@ using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using CoreSize = SixLabors.Primitives.Size; @@ -45,23 +45,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark(Description = "Decode Jpeg - ImageSharp")] - public CoreSize JpegImageSharpOrig() - { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - using (var image = Image.Load(memoryStream, new GolangJpegDecoder())) - { - return new CoreSize(image.Width, image.Height); - } - } - } - - [Benchmark(Description = "Decode Jpeg - ImageSharp PdfJs")] - public CoreSize JpegImageSharpPdfJs() + public CoreSize JpegImageSharp() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - using (var image = Image.Load(memoryStream, new PdfJsJpegDecoder())) + using (var image = Image.Load(memoryStream, new JpegDecoder())) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs index c4ee732f5..be0fe76b8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs @@ -3,8 +3,7 @@ using System.Collections.Generic; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SDImage = System.Drawing.Image; @@ -22,15 +21,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg protected override IEnumerable SearchPatterns => new[] { "*.jpg" }; [Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] - public void DecodeJpegImageSharpOrig() + public void DecodeJpegImageSharp() { - this.ForEachStream(ms => Image.Load(ms, new GolangJpegDecoder())); - } - - [Benchmark(Description = "DecodeJpegMultiple - ImageSharp PDFJs")] - public void DecodeJpegImageSharpPdfJs() - { - this.ForEachStream(ms => Image.Load(ms, new PdfJsJpegDecoder())); + this.ForEachStream(ms => Image.Load(ms, new JpegDecoder())); } [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 059f312b3..5958b9f79 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -5,7 +5,6 @@ using BenchmarkDotNet.Attributes; using System.Drawing; using System.IO; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg @@ -38,12 +37,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } } - [Benchmark(Description = "PdfJsJpegDecoderCore.ParseStream")] + [Benchmark(Description = "JpegDecoderCore.ParseStream")] public void ParseStreamPdfJs() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder() { IgnoreMetadata = true }); + var decoder = new JpegDecoderCore(Configuration.Default, new Formats.Jpeg.JpegDecoder() { IgnoreMetadata = true }); decoder.ParseStream(memoryStream); decoder.Dispose(); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs index f91595df8..c70378464 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs @@ -4,16 +4,17 @@ using System; using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { [Config(typeof(Config.ShortClr))] public class DoubleBufferedStreams { - private byte[] buffer = CreateTestBytes(); - private byte[] chunk1 = new byte[2]; - private byte[] chunk2 = new byte[2]; + private readonly byte[] buffer = CreateTestBytes(); + private readonly byte[] chunk1 = new byte[2]; + private readonly byte[] chunk2 = new byte[2]; private MemoryStream stream1; private MemoryStream stream2; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs index 53f0630b9..d934a1d6d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] public void JpegSystemDrawing() { - using (MemoryStream memoryStream = new MemoryStream()) + using (var memoryStream = new MemoryStream()) { this.bmpDrawing.Save(memoryStream, ImageFormat.Jpeg); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark(Description = "ImageSharp Jpeg")] public void JpegCore() { - using (MemoryStream memoryStream = new MemoryStream()) + using (var memoryStream = new MemoryStream()) { this.bmpCore.SaveAsJpeg(memoryStream); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index b6ad20d12..ae32167a9 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -3,8 +3,8 @@ using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg @@ -29,22 +29,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark] - public IImageInfo IdentifyGolang() - { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - var decoder = new GolangJpegDecoder(); - - return decoder.Identify(Configuration.Default, memoryStream); - } - } - - [Benchmark] - public IImageInfo IdentifyPdfJs() + public IImageInfo Identify() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - var decoder = new PdfJsJpegDecoder(); + var decoder = new JpegDecoder(); return decoder.Identify(Configuration.Default, memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs index 296b3fb7a..1d485ee08 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [Params( - TestImages.Jpeg.Baseline.Jpeg420Exif + TestImages.Jpeg.Baseline.Jpeg420Exif //, TestImages.Jpeg.Baseline.Calliphora )] public string TestImage { get; set; } @@ -74,10 +74,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark] public void ImageSharp() { - using (var source = Image.Load( - this.configuration, - this.sourceBytes, - new JpegDecoder { IgnoreMetadata = true })) + var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true }); + using (source) using (var destStream = new MemoryStream(this.destBytes)) { source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs index 20b199441..5316ec758 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs @@ -3,7 +3,8 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index e266554d1..8169fdba3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.Memory; + using SixLabors.ImageSharp.PixelFormats; using Xunit; // ReSharper disable InconsistentNaming @@ -13,33 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { [Theory] [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_Orig(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (SkipTest(provider)) - { - return; - } - - // For 32 bit test enviroments: - provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - using (Image image = provider.GetImage(GolangJpegDecoder)) - { - image.DebugSave(provider); - provider.Utility.TestName = DecodeBaselineJpegOutputName; - image.CompareToReferenceOutput( - this.GetImageComparer(provider), - provider, - appendPixelTypeToFileName: false); - } - - provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); - } - - [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_PdfJs(TestImageProvider provider) + public void DecodeBaselineJpeg(TestImageProvider provider) where TPixel : struct, IPixel { if (SkipTest(provider)) @@ -48,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return; } - using (Image image = provider.GetImage(PdfJsJpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder)) { image.DebugSave(provider); @@ -62,20 +36,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow_Golang(TestImageProvider provider) - where TPixel : struct, IPixel - { - // TODO: We need a public ImageDecoderException class in ImageSharp! - Assert.ThrowsAny(() => provider.GetImage(GolangJpegDecoder)); - } - - [Theory] - [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow_PdfJs(TestImageProvider provider) + public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow(TestImageProvider provider) where TPixel : struct, IPixel { // TODO: We need a public ImageDecoderException class in ImageSharp! - Assert.ThrowsAny(() => provider.GetImage(PdfJsJpegDecoder)); + Assert.ThrowsAny(() => provider.GetImage(JpegDecoder)); } [Theory(Skip = "Debug only, enable manually!")] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs index f217f0df1..2e67c06c1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(MetaDataTestData))] - public void MetaDataIsParsedCorrectly_Orig( + public void MetaDataIsParsedCorrectly( bool useIdentify, string imagePath, int expectedPixelSize, @@ -60,25 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { TestMetaDataImpl( useIdentify, - GolangJpegDecoder, - imagePath, - expectedPixelSize, - exifProfilePresent, - iccProfilePresent); - } - - [Theory] - [MemberData(nameof(MetaDataTestData))] - public void MetaDataIsParsedCorrectly_PdfJs( - bool useIdentify, - string imagePath, - int expectedPixelSize, - bool exifProfilePresent, - bool iccProfilePresent) - { - TestMetaDataImpl( - useIdentify, - PdfJsJpegDecoder, + JpegDecoder, imagePath, expectedPixelSize, exifProfilePresent, @@ -216,7 +198,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(true)] public void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) { - TestImageInfo(TestImages.Jpeg.Baseline.Floorplan, DefaultJpegDecoder, useIdentify, + TestImageInfo(TestImages.Jpeg.Baseline.Floorplan, JpegDecoder, useIdentify, imageInfo => { Assert.Equal(300, imageInfo.MetaData.HorizontalResolution); @@ -229,7 +211,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(true)] public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) { - TestImageInfo(TestImages.Jpeg.Baseline.Jpeg420Exif, DefaultJpegDecoder, useIdentify, + TestImageInfo(TestImages.Jpeg.Baseline.Jpeg420Exif, JpegDecoder, useIdentify, imageInfo => { Assert.Equal(72, imageInfo.MetaData.HorizontalResolution); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index c793b4034..9de788be2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Linq; -using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats; using Xunit; // ReSharper disable InconsistentNaming @@ -15,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] - public void DecodeProgressiveJpeg_Orig(TestImageProvider provider) + public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : struct, IPixel { if (SkipTest(provider)) @@ -24,41 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return; } - // Golang decoder is unable to decode these: - if (PdfJsOnly.Any(fn => fn.Contains(provider.SourceFileOrDescription))) - { - return; - } - - // For 32 bit test enviroments: - provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - using (Image image = provider.GetImage(GolangJpegDecoder)) - { - image.DebugSave(provider); - - provider.Utility.TestName = DecodeProgressiveJpegOutputName; - image.CompareToReferenceOutput( - this.GetImageComparer(provider), - provider, - appendPixelTypeToFileName: false); - } - - provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); - } - - [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] - public void DecodeProgressiveJpeg_PdfJs(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (SkipTest(provider)) - { - // skipping to avoid OutOfMemoryException on CI - return; - } - - using (Image image = provider.GetImage(PdfJsJpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder)) { image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 41cc6db51..496891ce4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -2,14 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; @@ -64,11 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg private ITestOutputHelper Output { get; } - private static GolangJpegDecoder GolangJpegDecoder => new GolangJpegDecoder(); - - private static PdfJsJpegDecoder PdfJsJpegDecoder => new PdfJsJpegDecoder(); - - private static JpegDecoder DefaultJpegDecoder => new JpegDecoder(); + private static JpegDecoder JpegDecoder => new JpegDecoder(); [Fact] public void ParseStream_BasicPropertiesAreCorrect1_PdfJs() @@ -76,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; using (var ms = new MemoryStream(bytes)) { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); decoder.ParseStream(ms); // I don't know why these numbers are different. All I know is that the decoder works @@ -89,9 +81,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; [Theory] - [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, false)] - [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, true)] - public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider, bool useOldDecoder) + [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes)] + public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { if (SkipTest(provider)) @@ -102,8 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // For 32 bit test enviroments: provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - IImageDecoder decoder = useOldDecoder ? (IImageDecoder)GolangJpegDecoder : PdfJsJpegDecoder; - using (Image image = provider.GetImage(decoder)) + using (Image image = provider.GetImage(JpegDecoder)) { image.DebugSave(provider); @@ -125,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg appendPixelTypeToFileName: false ).SingleOrDefault(); - if (report != null && report.TotalNormalizedDifference.HasValue) + if (report?.TotalNormalizedDifference != null) { return report.DifferencePercentageString; } @@ -139,17 +129,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.Output.WriteLine(provider.SourceFileOrDescription); provider.Utility.TestName = testName; - using (Image image = provider.GetImage(GolangJpegDecoder)) - { - string d = this.GetDifferenceInPercentageString(image, provider); - - this.Output.WriteLine($"Difference using ORIGINAL decoder: {d}"); - } - - using (Image image = provider.GetImage(PdfJsJpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder)) { string d = this.GetDifferenceInPercentageString(image, provider); - this.Output.WriteLine($"Difference using PDFJS decoder: {d}"); + this.Output.WriteLine($"Difference using decoder: {d}"); } } @@ -175,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) - using (var pdfJsPortResult = Image.Load(sourceBytes, PdfJsJpegDecoder)) + using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) { ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index 843843f79..cfa421a82 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -1,9 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -48,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg where TPixel : struct, IPixel { string imageFile = provider.SourceFileOrDescription; - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryAllocator, decoder)) using (var imageFrame = new ImageFrame(Configuration.Default, decoder.ImageWidth, decoder.ImageHeight)) { @@ -68,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg where TPixel : struct, IPixel { string imageFile = provider.SourceFileOrDescription; - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryAllocator, decoder)) using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index b0f342f5a..32538090d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -8,8 +8,6 @@ using System.Numerics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -34,18 +32,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, }; - //[Theory] // Benchmark, enable manually - //[MemberData(nameof(DecodeJpegData))] - public void DecodeJpeg_Original(string fileName) - { - this.DecodeJpegBenchmarkImpl(fileName, new GolangJpegDecoder()); - } - // [Theory] // Benchmark, enable manually // [MemberData(nameof(DecodeJpegData))] - public void DecodeJpeg_PdfJs(string fileName) + public void DecodeJpeg(string fileName) { - this.DecodeJpegBenchmarkImpl(fileName, new PdfJsJpegDecoder()); + this.DecodeJpegBenchmarkImpl(fileName, new JpegDecoder()); } private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder) @@ -70,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg ExecutionCount, () => { - Image img = Image.Load(bytes, decoder); + var img = Image.Load(bytes, decoder); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs index 827a459cd..e4d8d29d4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -2,12 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System.Text; + +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.Primitives; @@ -30,53 +28,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.Grayscale)] [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)] [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] - public void ColorSpace_IsDeducedCorrectlyGolang(string imageFile, object expectedColorSpaceValue) + public void ColorSpace_IsDeducedCorrectly(string imageFile, object expectedColorSpaceValue) { var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue; - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { Assert.Equal(expecteColorSpace, decoder.ColorSpace); } } - [Theory] - [InlineData(TestImages.Jpeg.Baseline.Testorig420, JpegColorSpace.YCbCr)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.Grayscale)] - [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)] - [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] - public void ColorSpace_IsDeducedCorrectlyPdfJs(string imageFile, object expectedColorSpaceValue) - { - var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue; - - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) - { - Assert.Equal(expecteColorSpace, decoder.ColorSpace); - } - } - - [Fact] - public void ComponentScalingIsCorrect_1ChannelJpegGolang() - { - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(TestImages.Jpeg.Baseline.Jpeg400)) - { - Assert.Equal(1, decoder.ComponentCount); - Assert.Equal(1, decoder.Components.Length); - - Size expectedSizeInBlocks = decoder.ImageSizeInPixels.DivideRoundUp(8); - - Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); - - var uniform1 = new Size(1, 1); - GolangComponent c0 = decoder.Components[0]; - VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); - } - } - [Fact] - public void ComponentScalingIsCorrect_1ChannelJpegPdfJs() + public void ComponentScalingIsCorrect_1ChannelJpeg() { - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(TestImages.Jpeg.Baseline.Jpeg400)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(TestImages.Jpeg.Baseline.Jpeg400)) { Assert.Equal(1, decoder.ComponentCount); Assert.Equal(1, decoder.Components.Length); @@ -86,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); var uniform1 = new Size(1, 1); - PdfJsFrameComponent c0 = decoder.Components[0]; + JpegFrameComponent c0 = decoder.Components[0]; VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); } } @@ -98,40 +63,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(TestImages.Jpeg.Baseline.Testorig420)] [InlineData(TestImages.Jpeg.Baseline.Ycck)] [InlineData(TestImages.Jpeg.Baseline.Cmyk)] - public void PrintComponentDataGolang(string imageFile) - { - var sb = new StringBuilder(); - - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile)) - { - sb.AppendLine(imageFile); - sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); - GolangComponent c0 = decoder.Components[0]; - GolangComponent c1 = decoder.Components[1]; - - sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); - sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); - } - this.Output.WriteLine(sb.ToString()); - } - - [Theory] - [InlineData(TestImages.Jpeg.Baseline.Jpeg444)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small)] - [InlineData(TestImages.Jpeg.Baseline.Testorig420)] - [InlineData(TestImages.Jpeg.Baseline.Ycck)] - [InlineData(TestImages.Jpeg.Baseline.Cmyk)] - public void PrintComponentDataPdfJs(string imageFile) + public void PrintComponentData(string imageFile) { var sb = new StringBuilder(); - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { sb.AppendLine(imageFile); sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); - PdfJsFrameComponent c0 = decoder.Components[0]; - PdfJsFrameComponent c1 = decoder.Components[1]; + JpegFrameComponent c0 = decoder.Components[0]; + JpegFrameComponent c1 = decoder.Components[1]; sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); @@ -152,48 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(ComponentVerificationData))] - public void ComponentScalingIsCorrect_MultiChannelJpegGolang( - string imageFile, - int componentCount, - object expectedLumaFactors, - object expectedChromaFactors) - { - var fLuma = (Size)expectedLumaFactors; - var fChroma = (Size)expectedChromaFactors; - - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile)) - { - Assert.Equal(componentCount, decoder.ComponentCount); - Assert.Equal(componentCount, decoder.Components.Length); - - GolangComponent c0 = decoder.Components[0]; - GolangComponent c1 = decoder.Components[1]; - GolangComponent c2 = decoder.Components[2]; - - var uniform1 = new Size(1, 1); - - Size expectedLumaSizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(fLuma); - - Size divisor = fLuma.DivideBy(fChroma); - - Size expectedChromaSizeInBlocks = expectedLumaSizeInBlocks.DivideRoundUp(divisor); - - VerifyJpeg.VerifyComponent(c0, expectedLumaSizeInBlocks, fLuma, uniform1); - VerifyJpeg.VerifyComponent(c1, expectedChromaSizeInBlocks, fChroma, divisor); - VerifyJpeg.VerifyComponent(c2, expectedChromaSizeInBlocks, fChroma, divisor); - - if (componentCount == 4) - { - GolangComponent c3 = decoder.Components[2]; - VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); - } - } - } - - - [Theory] - [MemberData(nameof(ComponentVerificationData))] - public void ComponentScalingIsCorrect_MultiChannelJpegPdfJs( + public void ComponentScalingIsCorrect_MultiChannelJpeg( string imageFile, int componentCount, object expectedLumaFactors, @@ -202,14 +102,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var fLuma = (Size)expectedLumaFactors; var fChroma = (Size)expectedChromaFactors; - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { Assert.Equal(componentCount, decoder.ComponentCount); Assert.Equal(componentCount, decoder.Components.Length); - PdfJsFrameComponent c0 = decoder.Components[0]; - PdfJsFrameComponent c1 = decoder.Components[1]; - PdfJsFrameComponent c2 = decoder.Components[2]; + JpegFrameComponent c0 = decoder.Components[0]; + JpegFrameComponent c1 = decoder.Components[1]; + JpegFrameComponent c2 = decoder.Components[2]; var uniform1 = new Size(1, 1); @@ -225,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg if (componentCount == 4) { - PdfJsFrameComponent c3 = decoder.Components[2]; + JpegFrameComponent c3 = decoder.Components[2]; VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 46a688b49..9378b1c8e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -4,8 +4,6 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; @@ -42,10 +40,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory(Skip = "Debug only, enable manually!")] [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void PdfJsDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + public void Decoder_ParseStream_SaveSpectralResult(TestImageProvider provider) where TPixel : struct, IPixel { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; @@ -58,25 +56,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - [Theory(Skip = "Debug only, enable manually!")] + [Theory] [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void OriginalDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + public void VerifySpectralCorrectness(TestImageProvider provider) where TPixel : struct, IPixel { - var decoder = new GolangJpegDecoderCore(Configuration.Default, new JpegDecoder()); + if (!TestEnvironment.IsWindows) + { + return; + } + + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; using (var ms = new MemoryStream(sourceBytes)) { - decoder.ParseStream(ms, false); + decoder.ParseStream(ms); + var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - VerifyJpeg.SaveSpectralImage(provider, data); + this.VerifySpectralCorrectnessImpl(provider, imageSharpData); } } - - private void VerifySpectralCorrectness( + + private void VerifySpectralCorrectnessImpl( TestImageProvider provider, LibJpegTools.SpectralData imageSharpData) where TPixel : struct, IPixel @@ -119,51 +122,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.True(totalDifference < tolerance); } - - [Theory] - [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void VerifySpectralCorrectness_PdfJs(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (!TestEnvironment.IsWindows) - { - return; - } - - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); - - byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; - - using (var ms = new MemoryStream(sourceBytes)) - { - decoder.ParseStream(ms); - var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - - this.VerifySpectralCorrectness(provider, imageSharpData); - } - } - - [Theory] - [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void VerifySpectralCorrectness_Golang(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (!TestEnvironment.IsWindows) - { - return; - } - - var decoder = new GolangJpegDecoderCore(Configuration.Default, new GolangJpegDecoder()); - - byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; - - using (var ms = new MemoryStream(sourceBytes)) - { - decoder.ParseStream(ms); - var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - - this.VerifySpectralCorrectness(provider, imageSharpData); - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs index 7fe98e2af..d14fbc3fc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -10,8 +10,6 @@ using System.Text; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using Xunit; using Xunit.Abstractions; @@ -175,23 +173,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils Assert.False(failed); } - internal static GolangJpegDecoderCore ParseGolangStream(string testFileName, bool metaDataOnly = false) + internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDataOnly = false) { byte[] bytes = TestFile.Create(testFileName).Bytes; using (var ms = new MemoryStream(bytes)) { - var decoder = new GolangJpegDecoderCore(Configuration.Default, new JpegDecoder()); - decoder.ParseStream(ms, metaDataOnly); - return decoder; - } - } - - internal static PdfJsJpegDecoderCore ParsePdfJsStream(string testFileName, bool metaDataOnly = false) - { - byte[] bytes = TestFile.Create(testFileName).Bytes; - using (var ms = new MemoryStream(bytes)) - { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); decoder.ParseStream(ms, metaDataOnly); return decoder; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 645ee4512..5ffd5d62b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -1,18 +1,15 @@ +using System; +using System.Linq; +using System.Numerics; + using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { - using System; - using System.Linq; - using System.Numerics; - - using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; - using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; - using SixLabors.Memory; - using SixLabors.Primitives; - internal static partial class LibJpegTools { /// @@ -57,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.SpectralBlocks[x, y] = new Block8x8(data); } - public static ComponentData Load(PdfJsFrameComponent c, int index) + public static ComponentData Load(JpegFrameComponent c, int index) { var result = new ComponentData( c.WidthInBlocks, @@ -77,26 +74,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils return result; } - public static ComponentData Load(GolangComponent c) - { - var result = new ComponentData( - c.SizeInBlocks.Width, - c.SizeInBlocks.Height, - c.Index - ); - - for (int y = 0; y < result.HeightInBlocks; y++) - { - for (int x = 0; x < result.WidthInBlocks; x++) - { - short[] data = c.GetBlockReference(x, y).ToArray(); - result.MakeBlock(data, y, x); - } - } - - return result; - } - public Image CreateGrayScaleImage() { var result = new Image(this.WidthInBlocks * 8, this.HeightInBlocks * 8); @@ -143,8 +120,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public bool Equals(ComponentData other) { - if (object.ReferenceEquals(null, other)) return false; - if (object.ReferenceEquals(this, other)) return true; + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + bool ok = this.Index == other.Index && this.HeightInBlocks == other.HeightInBlocks && this.WidthInBlocks == other.WidthInBlocks; //&& this.MinVal == other.MinVal @@ -165,8 +150,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public override bool Equals(object obj) { - if (Object.ReferenceEquals(null, obj)) return false; - if (Object.ReferenceEquals(this, obj)) return true; + if (obj is null) return false; + if (object.ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return this.Equals((ComponentData)obj); } @@ -175,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { unchecked { - var hashCode = this.Index; + int hashCode = this.Index; hashCode = (hashCode * 397) ^ this.HeightInBlocks; hashCode = (hashCode * 397) ^ this.WidthInBlocks; hashCode = (hashCode * 397) ^ this.MinVal.GetHashCode(); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index ae8194e1a..e9f0b1138 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -5,11 +5,9 @@ using System; using System.Linq; using System.Numerics; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils @@ -32,17 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.Components = components; } - public static SpectralData LoadFromImageSharpDecoder(PdfJsJpegDecoderCore decoder) + public static SpectralData LoadFromImageSharpDecoder(JpegDecoderCore decoder) { - PdfJsFrameComponent[] srcComponents = decoder.Frame.Components; - LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); - - return new SpectralData(destComponents); - } - - public static SpectralData LoadFromImageSharpDecoder(GolangJpegDecoderCore decoder) - { - GolangComponent[] srcComponents = decoder.Components; + JpegFrameComponent[] srcComponents = decoder.Frame.Components; LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); return new SpectralData(destComponents); @@ -108,8 +98,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public bool Equals(SpectralData other) { - if (object.ReferenceEquals(null, other)) return false; - if (object.ReferenceEquals(this, other)) return true; + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + if (this.ComponentCount != other.ComponentCount) { return false;