diff --git a/ColorSpaceGenerator/ColorSpaceGenerator.csproj b/ColorSpaceGenerator/ColorSpaceGenerator.csproj new file mode 100644 index 0000000000..7727272f8f --- /dev/null +++ b/ColorSpaceGenerator/ColorSpaceGenerator.csproj @@ -0,0 +1 @@ + diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs index 26bcde8e51..a7ec93eaf7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs @@ -20,29 +20,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// Initializes a new instance of the class. /// /// The memory allocator used to allocate memory for image processing operations. - public FastACTables(MemoryAllocator memoryAllocator) - { - this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean); - } + public FastACTables(MemoryAllocator memoryAllocator) => this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean); /// /// Gets the representing the table at the index in the collection. /// /// The table index. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan GetTableSpan(int index) - { - return this.tables.GetRowSpan(index); - } + [MethodImpl(InliningOptions.ShortMethod)] + public ReadOnlySpan GetTableSpan(int index) => this.tables.GetRowSpan(index); /// - /// Gets a reference to the first element of the AC table indexed by /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetAcTableReference(JpegComponent component) - { - return ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; - } + /// Gets a reference to the first element of the AC table indexed by + /// + /// The frame component. + [MethodImpl(InliningOptions.ShortMethod)] + public ref short GetAcTableReference(JpegComponent component) => ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; /// /// Builds a lookup table for fast AC entropy scan decoding. @@ -67,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int magbits = rs & 15; int len = huffman.Sizes[fast]; - if (magbits > 0 && len + magbits <= FastBits) + if (magbits != 0 && len + magbits <= FastBits) { // Magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); @@ -80,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // if the result is small enough, we can fit it in fastAC table if (k >= -128 && k <= 127) { - fastAC[i] = (short)((k * 256) + (run * 16) + (len + magbits)); + fastAC[i] = (short)((k << 8) + (run << 4) + (len + magbits)); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 0138164ed2..a6bf1bd953 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -64,8 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder byte l = count[i]; for (short j = 0; j < l; j++) { - sizesRef[x] = i; - x++; + sizesRef[x++] = i; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index da089fa44a..36a3dc2d26 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -45,6 +45,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// public byte[] ComponentIds { get; set; } + /// + /// Gets or sets the order in which to process the components + /// in interleaved mode. + /// + public byte[] ComponentOrder { get; set; } + /// /// Gets or sets the frame component collection /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 8c525335bc..6741ccdac2 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -34,9 +34,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // The restart interval. private readonly int restartInterval; - // The current component index. - private readonly int componentIndex; - // The number of interleaved components. private readonly int componentsLength; @@ -87,7 +84,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The DC Huffman tables. /// The AC Huffman tables. /// The fast AC decoding tables. - /// The component index within the array. /// The length of the components. Different to the array length. /// The reset interval. /// The spectral selection start. @@ -100,7 +96,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder HuffmanTables dcHuffmanTables, HuffmanTables acHuffmanTables, FastACTables fastACTables, - int componentIndex, int componentsLength, int restartInterval, int spectralStart, @@ -117,7 +112,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.components = frame.Components; this.marker = JpegConstants.Markers.XFF; this.markerPosition = 0; - this.componentIndex = componentIndex; this.componentsLength = componentsLength; this.restartInterval = restartInterval; this.spectralStart = spectralStart; @@ -176,7 +170,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - JpegComponent component = this.components[k]; + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; @@ -223,14 +218,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Non-interleaved data, we just need to process one block at a ti - /// in trivial scanline order - /// number of blocks to do just depends on how many actual "pixels" - /// component has, independent of interleaved MCU blocking and such + /// Non-interleaved data, we just need to process one block at a time in trivial scanline order + /// number of blocks to do just depends on how many actual "pixels" each component has, + /// independent of interleaved MCU blocking and such. /// private void ParseBaselineDataNonInterleaved() { - JpegComponent component = this.components[this.componentIndex]; + JpegComponent component = this.components[this.frame.ComponentOrder[0]]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -295,7 +289,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - JpegComponent component = this.components[k]; + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -344,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// private void ParseProgressiveDataNonInterleaved() { - JpegComponent component = this.components[this.componentIndex]; + JpegComponent component = this.components[this.frame.ComponentOrder[0]]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -729,8 +724,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } uint k = LRot(this.codeBuffer, n); - this.codeBuffer = k & ~Bmask[n]; - k &= Bmask[n]; + uint mask = Bmask[n]; + this.codeBuffer = k & ~mask; + k &= mask; this.codeBits -= n; return (int)k; } @@ -839,7 +835,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // that way we don't need to shift inside the loop. uint temp = this.codeBuffer >> 16; int k; - for (k = FastBits + 1; ; k++) + for (k = FastBits + 1; ; ++k) { if (temp < table.MaxCode[k]) { diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index e3b0b4bdc6..22d9cbdee4 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -747,11 +747,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (!metadataOnly) { // No need to pool this. They max out at 4 - this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; - this.Frame.Components = new JpegComponent[this.Frame.ComponentCount]; + this.Frame.ComponentIds = new byte[this.ComponentCount]; + this.Frame.ComponentOrder = new byte[this.ComponentCount]; + this.Frame.Components = new JpegComponent[this.ComponentCount]; this.ColorSpace = this.DeduceJpegColorSpace(); - for (int i = 0; i < this.Frame.ComponentCount; i++) + for (int i = 0; i < this.ComponentCount; i++) { byte hv = this.temp[index + 1]; int h = hv >> 4; @@ -823,10 +824,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg codeLengths.GetSpan(), huffmanValues.GetSpan()); - if (huffmanTableSpec >> 4 != 0) + if (tableType != 0) { // Build a table that decodes both magnitude and value of small ACs in one go. - this.fastACTables.BuildACTableLut(huffmanTableSpec & 15, this.acHuffmanTables); + this.fastACTables.BuildACTableLut(tableIndex, this.acHuffmanTables); } } } @@ -867,6 +868,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (selector == id) { componentIndex = j; + break; } } @@ -879,6 +881,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int tableSpec = this.InputStream.ReadByte(); component.DCHuffmanTableId = tableSpec >> 4; component.ACHuffmanTableId = tableSpec & 15; + this.Frame.ComponentOrder[i] = (byte)componentIndex; } this.InputStream.Read(this.temp, 0, 3); @@ -893,7 +896,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.dcHuffmanTables, this.acHuffmanTables, this.fastACTables, - componentIndex, selectorsCount, this.resetInterval, spectralStart, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 2f041e3ab4..6bc559978c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -44,6 +44,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.BadRstProgressive518, TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, TestImages.Jpeg.Issues.DhtHasWrongLength624, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C }; /// diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 309fb1d4ab..fdf586c430 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -156,6 +156,9 @@ namespace SixLabors.ImageSharp.Tests public const string InvalidEOI695 = "Jpg/issues/Issue695-Invalid-EOI.jpg"; public const string ExifResizeOutOfRange696 = "Jpg/issues/Issue696-Resize-Exif-OutOfRange.jpg"; public const string InvalidAPP0721 = "Jpg/issues/Issue721-InvalidAPP0.jpg"; + public const string OrderedInterleavedProgressive723A = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg"; + public const string OrderedInterleavedProgressive723B = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg"; + public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg"; } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); diff --git a/tests/Images/External b/tests/Images/External index 5f3cbd839f..c0627f384c 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 5f3cbd839fbbffae615d294d1dabafdcabc64cf9 +Subproject commit c0627f384c1d3d2f8d914c9578ae31354c35fd2c diff --git a/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg new file mode 100644 index 0000000000..0a11065ce9 Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg new file mode 100644 index 0000000000..eb52570e1c Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg new file mode 100644 index 0000000000..0224cb7f1f Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg differ