From 97685f1c425fcde34e7655f114474ce2a16a7caa Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 22 Aug 2017 02:45:29 +0200 Subject: [PATCH] refactor OldComponent: it's class again + got moved in some logic --- .../Components/Decoder/JpegBlockProcessor.cs | 2 +- .../Components/Decoder/OldComponent.cs | 199 +++++++++++++++++- .../Components/Decoder/OldJpegScanDecoder.cs | 10 +- .../Jpeg/GolangPort/OldJpegDecoderCore.cs | 188 +++-------------- 4 files changed, 220 insertions(+), 179 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockProcessor.cs index ba4a42127..a6c4e4f1a 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockProcessor.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder private void ProcessBlockColors(OldJpegDecoderCore decoder, ref DecodedBlock decodedBlock) { this.data.Block = decodedBlock.Block; - int qtIndex = decoder.ComponentArray[this.componentIndex].Selector; + int qtIndex = decoder.Components[this.componentIndex].Selector; this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; Block8x8F* b = this.pointers.Block; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldComponent.cs index 3f1f70b20..dca131961 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldComponent.cs @@ -3,29 +3,210 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { + using System; + + using SixLabors.ImageSharp.Memory; + /// /// Represents a single color component /// - internal struct OldComponent + internal class OldComponent { + public OldComponent(byte identifier, int index) + { + this.Identifier = identifier; + this.Index = index; + } + /// - /// Gets or sets the horizontal sampling factor. + /// Gets the identifier /// - public int HorizontalFactor; + public byte Identifier { get; } /// - /// Gets or sets the vertical sampling factor. + /// Gets the component's position in /// - public int VerticalFactor; + public int Index { get; } /// - /// Gets or sets the identifier + /// Gets the horizontal sampling factor. /// - public byte Identifier; + public int HorizontalFactor { get; private set; } /// - /// Gets or sets the quantization table destination selector. + /// Gets the vertical sampling factor. /// - public byte Selector; + public int VerticalFactor { get; private set; } + + /// + /// Gets the quantization table destination selector. + /// + public byte Selector { get; private set; } + + public Buffer DecodedBlocks { get; private set; } + + /// + /// Initializes + /// + /// The instance + public void InitializeBlocks(OldJpegDecoderCore decoder) + { + // TODO: count could be component and JpegSubsample specific: + int count = decoder.TotalMCUCount * this.HorizontalFactor * this.VerticalFactor; + this.DecodedBlocks = Buffer.CreateClean(count); + } + + /// + /// Initializes all component data except . + /// + /// The instance + public void InitializeData(OldJpegDecoderCore 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.Selector = decoder.Temp[8 + (3 * i)]; + if (this.Selector > OldJpegDecoderCore.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. + if (decoder.Components[0].HorizontalFactor % h != 0 + || decoder.Components[0].VerticalFactor % v != 0) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + case 2: + { + // Cr. + if (decoder.Components[1].HorizontalFactor != h + || decoder.Components[1].VerticalFactor != 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: + if (decoder.Components[0].HorizontalFactor != h + || decoder.Components[0].VerticalFactor != v) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + break; + } + + this.HorizontalFactor = h; + this.VerticalFactor = v; + } } } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.cs index 81e9a5034..7fd6276d8 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.cs @@ -147,8 +147,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) { this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - this.hi = decoder.ComponentArray[this.ComponentIndex].HorizontalFactor; - int vi = decoder.ComponentArray[this.ComponentIndex].VerticalFactor; + this.hi = decoder.Components[this.ComponentIndex].HorizontalFactor; + int vi = decoder.Components[this.ComponentIndex].VerticalFactor; for (int j = 0; j < this.hi * vi; j++) { @@ -440,7 +440,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder for (int j = 0; j < decoder.ComponentCount; j++) { // Component compv = ; - if (cs == decoder.ComponentArray[j].Identifier) + if (cs == decoder.Components[j].Identifier) { compIndex = j; } @@ -453,7 +453,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder currentComponentScan.ComponentIndex = (byte)compIndex; - this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); + this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, decoder.Components[compIndex]); } private void ProcessComponentImpl( @@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder int i, ref OldComponentScan currentComponentScan, ref int totalHv, - ref OldComponent currentComponent) + OldComponent 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 diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OldJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OldJpegDecoderCore.cs index 765c5f39a..250e6e0d2 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OldJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OldJpegDecoderCore.cs @@ -107,14 +107,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.HuffmanTrees = OldHuffmanTree.CreateHuffmanTrees(); this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; - this.ComponentArray = new OldComponent[MaxComponents]; this.DecodedBlocks = new Buffer[MaxComponents]; } /// /// Gets the component array /// - public OldComponent[] ComponentArray { get; } + public OldComponent[] Components { get; private set; } /// /// Gets the huffman trees @@ -576,7 +575,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort private void ConvertFromCmyk(Image image) where TPixel : struct, IPixel { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; + int scale = this.Components[0].HorizontalFactor / this.Components[1].HorizontalFactor; using (PixelAccessor pixels = image.Lock()) { @@ -642,7 +641,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort private void ConvertFromRGB(Image image) where TPixel : struct, IPixel { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; + int scale = this.Components[0].HorizontalFactor / this.Components[1].HorizontalFactor; Parallel.For( 0, @@ -679,7 +678,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort private void ConvertFromYCbCr(Image image) where TPixel : struct, IPixel { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; + int scale = this.Components[0].HorizontalFactor / this.Components[1].HorizontalFactor; using (PixelAccessor pixels = image.Lock()) { Parallel.For( @@ -724,7 +723,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort private void ConvertFromYcck(Image image) where TPixel : struct, IPixel { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; + int scale = this.Components[0].HorizontalFactor / this.Components[1].HorizontalFactor; Parallel.For( 0, @@ -770,8 +769,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort return true; } - return this.ComponentArray[0].Identifier == 'R' && this.ComponentArray[1].Identifier == 'G' - && this.ComponentArray[2].Identifier == 'B'; + return this.Components[0].Identifier == 'R' && this.Components[1].Identifier == 'G' + && this.Components[2].Identifier == 'B'; } /// @@ -791,10 +790,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort } else { - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; - int horizontalRatio = h0 / this.ComponentArray[1].HorizontalFactor; - int verticalRatio = v0 / this.ComponentArray[1].VerticalFactor; + int h0 = this.Components[0].HorizontalFactor; + int v0 = this.Components[0].VerticalFactor; + int horizontalRatio = h0 / this.Components[1].HorizontalFactor; + int verticalRatio = v0 / this.Components[1].VerticalFactor; YCbCrImage.YCbCrSubsampleRatio ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; switch ((horizontalRatio << 4) | verticalRatio) @@ -823,10 +822,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort if (this.ComponentCount == 4) { - int h3 = this.ComponentArray[3].HorizontalFactor; - int v3 = this.ComponentArray[3].VerticalFactor; + int h3 = this.Components[3].HorizontalFactor; + int v3 = this.Components[3].VerticalFactor; - Buffer2D buffer = Buffer2D.CreateClean(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY); + var buffer = Buffer2D.CreateClean(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY); this.blackImage = new OldJpegPixelArea(buffer); } } @@ -1208,165 +1207,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort throw new ImageFormatException("SOF has wrong length"); } + this.Components = new OldComponent[this.ComponentCount]; + for (int i = 0; i < this.ComponentCount; i++) { - this.ComponentArray[i].Identifier = this.Temp[6 + (3 * i)]; - - // Section B.2.2 states that "the value of C_i shall be different from - // the values of C_1 through C_(i-1)". - for (int j = 0; j < i; j++) - { - if (this.ComponentArray[i].Identifier == this.ComponentArray[j].Identifier) - { - throw new ImageFormatException("Repeated component identifier"); - } - } - - this.ComponentArray[i].Selector = this.Temp[8 + (3 * i)]; - if (this.ComponentArray[i].Selector > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - byte hv = this.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 (this.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. - if (this.ComponentArray[0].HorizontalFactor % h != 0 - || this.ComponentArray[0].VerticalFactor % v != 0) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 2: - { - // Cr. - if (this.ComponentArray[1].HorizontalFactor != h - || this.ComponentArray[1].VerticalFactor != 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: - if (this.ComponentArray[0].HorizontalFactor != h - || this.ComponentArray[0].VerticalFactor != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - break; - } - - this.ComponentArray[i].HorizontalFactor = h; - this.ComponentArray[i].VerticalFactor = v; + byte componentIdentifier = this.Temp[6 + (3 * i)]; + var component = new OldComponent(componentIdentifier, i); + component.InitializeData(this); + this.Components[i] = component; } - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; + int h0 = this.Components[0].HorizontalFactor; + int v0 = this.Components[0].VerticalFactor; this.MCUCountX = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); this.MCUCountY = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); // As a preparation for parallelizing Scan decoder, we also allocate DecodedBlocks in the non-progressive case! for (int i = 0; i < this.ComponentCount; i++) { - int count = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor - * this.ComponentArray[i].VerticalFactor; + int count = this.TotalMCUCount * this.Components[i].HorizontalFactor + * this.Components[i].VerticalFactor; this.DecodedBlocks[i] = Buffer.CreateClean(count); } }