From a54e213c97f20f680abc7a1605fac7217fd36744 Mon Sep 17 00:00:00 2001 From: antonfirsov Date: Tue, 27 Dec 2016 05:39:28 +0100 Subject: [PATCH] JpegScanDecoder replaced the old stuff! --- ...derScanProcessor.cs => JpegScanDecoder.cs} | 171 +++-- src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs | 625 +----------------- 2 files changed, 110 insertions(+), 686 deletions(-) rename src/ImageSharp/Formats/Jpg/Components/Decoder/{DecoderScanProcessor.cs => JpegScanDecoder.cs} (79%) diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs similarity index 79% rename from src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs rename to src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs index a601d521b..664219b9b 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs @@ -1,11 +1,22 @@ -namespace ImageSharp.Formats.Jpg +// ReSharper disable InconsistentNaming +namespace ImageSharp.Formats.Jpg { using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - internal unsafe struct DecoderScanProcessor + internal unsafe struct JpegScanDecoder { + /// + /// The AC table index + /// + internal const int AcTableIndex = 1; + + /// + /// The DC table index + /// + internal const int DcTableIndex = 0; + [StructLayout(LayoutKind.Sequential)] public struct ComponentData { @@ -19,9 +30,9 @@ public UnzigData Unzig; - //public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents]; + public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents]; - //public fixed int Dc [JpegDecoderCore.MaxComponents]; + public fixed int Dc [JpegDecoderCore.MaxComponents]; public static ComponentData Create() { @@ -43,9 +54,9 @@ public int* Unzig; - //public Scan* Scan; + public Scan* Scan; - //public int* Dc; + public int* Dc; public ComponentPointers(ComponentData* basePtr) { @@ -54,46 +65,67 @@ this.Temp2 = &basePtr->Temp2; this.QuantiazationTable = &basePtr->QuantiazationTable; this.Unzig = basePtr->Unzig.Data; - //this.Scan = (Scan*) basePtr->ScanData; - //this.Dc = basePtr->Dc; + this.Scan = (Scan*)basePtr->ScanData; + this.Dc = basePtr->Dc; } } - //private void ResetDc() - //{ - // Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); - //} + private void ResetDc() + { + Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); + } + + + // bx and by are the location of the current block, in units of 8x8 + // blocks: the third block in the first row has (bx, by) = (2, 0). + + private int bx; + + private int by; - public int bx; + // zigStart and zigEnd are the spectral selection bounds. + // ah and al 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. - public int by; + private int zigStart; - public int zigStart; + private int zigEnd; - public int zigEnd; + private int ah; - public int ah; + private int al; - public int al; + // XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image. - public int mxx; + public int XNumberOfMCUs; - public int myy; + public int YNumberOfMCUs; - public int scanComponentCount; + private int scanComponentCount; - public ComponentData Data; + private ComponentData Data; - public ComponentPointers Pointers; + private ComponentPointers Pointers; - public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining, Scan[] scan) + public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) { p->Data = ComponentData.Create(); p->Pointers = new ComponentPointers(&p->Data); - p->InitImpl(decoder, remaining, scan); + p->InitImpl(decoder, remaining); } - private void InitImpl(JpegDecoderCore decoder, int remaining, Scan[] scan) + private void InitImpl(JpegDecoderCore decoder, int remaining) { if (decoder.ComponentCount == 0) { @@ -118,7 +150,7 @@ for (int i = 0; i < this.scanComponentCount; i++) { - this.ProcessScanImpl(decoder, i, ref scan[i], ref totalHv, scan); + this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[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. @@ -153,20 +185,20 @@ } } - // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. + // XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image. int h0 = decoder.ComponentArray[0].HorizontalFactor; int v0 = decoder.ComponentArray[0].VerticalFactor; - this.mxx = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0); - this.myy = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0); + this.XNumberOfMCUs = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0); + this.YNumberOfMCUs = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0); if (decoder.IsProgressive) { for (int i = 0; i < this.scanComponentCount; i++) { - int compIndex = scan[i].Index; + int compIndex = this.Pointers.Scan[i].Index; if (decoder.ProgCoeffs[compIndex] == null) { - int size = this.mxx * this.myy * decoder.ComponentArray[compIndex].HorizontalFactor + int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor * decoder.ComponentArray[compIndex].VerticalFactor; decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; @@ -175,7 +207,7 @@ } } - private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv, Scan[] scan) + private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv) { // Component selector. int cs = decoder.Temp[1 + (2 * i)]; @@ -196,7 +228,7 @@ currentScan.Index = (byte)compIndex; - this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex], scan); + this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); } @@ -205,7 +237,7 @@ int i, ref Scan currentScan, ref int totalHv, - ref Component currentComponent, Scan[] scan) + ref Component 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 @@ -214,7 +246,7 @@ // into comp are unique. for (int j = 0; j < i; j++) { - if (currentScan.Index == scan[j].Index) + if (currentScan.Index == this.Pointers.Scan[j].Index) { throw new ImageFormatException("Repeated component selector"); } @@ -235,19 +267,19 @@ } } - public void ProcessBlocks(JpegDecoderCore decoder, Scan[] scan, ref int[] dc) + public void ProcessBlocks(JpegDecoderCore decoder) { int blockCount = 0; int mcu = 0; byte expectedRst = JpegConstants.Markers.RST0; - for (int my = 0; my < this.myy; my++) + for (int my = 0; my < this.YNumberOfMCUs; my++) { - for (int mx = 0; mx < this.mxx; mx++) + for (int mx = 0; mx < this.XNumberOfMCUs; mx++) { for (int i = 0; i < this.scanComponentCount; i++) { - int compIndex = scan[i].Index; + int compIndex = this.Pointers.Scan[i].Index; int hi = decoder.ComponentArray[compIndex].HorizontalFactor; int vi = decoder.ComponentArray[compIndex].VerticalFactor; @@ -282,7 +314,7 @@ } else { - int q = this.mxx * hi; + int q = this.XNumberOfMCUs * hi; this.bx = blockCount % q; this.by = blockCount / q; blockCount++; @@ -301,7 +333,7 @@ //Load the previous partially decoded coefficients, if applicable. if (decoder.IsProgressive) { - int blockIndex = ((this.by * this.mxx) * hi) + this.bx; + int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx; this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; } else @@ -309,7 +341,7 @@ this.Data.Block.Clear(); } - this.ProcessBlockImpl(decoder, i, compIndex, hi, scan, ref dc); + this.ProcessBlockImpl(decoder, i, compIndex, hi); } // for j @@ -318,7 +350,7 @@ // for i mcu++; - if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.mxx * this.myy) + if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.XNumberOfMCUs * this.YNumberOfMCUs) { // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, // but this one assumes well-formed input, and hence the restart marker follows immediately. @@ -338,9 +370,8 @@ decoder.Bits = default(Bits); // Reset the DC components, as per section F.2.1.3.1. - //this.ResetDc(); - dc = new int[JpegDecoderCore.MaxComponents]; - + this.ResetDc(); + // Reset the progressive decoder state, as per section G.1.2.2. decoder.EobRun = 0; } @@ -351,15 +382,11 @@ } /// - /// The AC table index + /// Decodes a successive approximation refinement block, as specified in section G.1.2. /// - internal const int AcTable = 1; - - /// - /// The DC table index - /// - internal const int DcTable = 0; - + /// The Huffman tree + /// The low transform offset + /// The decoder instance private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta) { Block8x8F* b = this.Pointers.Block; @@ -430,9 +457,7 @@ { break; } - - int blah = zig; - + zig = this.RefineNonZeroes(decoder, zig, val0, delta); if (zig > this.zigEnd) { @@ -453,11 +478,20 @@ this.RefineNonZeroes(decoder, zig,-1, delta); } } - + + /// + /// Refines non-zero entries of b in zig-zag order. + /// If >= 0, the first zero entries are skipped over. + /// + /// The decoder + /// The zig-zag start index + /// The non-zero entry + /// The low transform offset + /// The private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta) { var b = this.Pointers.Block; - for (; zig <= zigEnd; zig++) + for (; zig <= this.zigEnd; zig++) { int u = this.Pointers.Unzig[zig]; float bu = Block8x8F.GetScalarAt(b, u); @@ -495,18 +529,18 @@ return zig; } - private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi, Scan[] scan, ref int[] dc) + private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi) { var b = this.Pointers.Block; //var dc = this.Pointers.Dc; - int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector; + int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].AcTableSelector; if (this.ah != 0) { this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); } else { - int zig = zigStart; + int zig = this.zigStart; if (zig == 0) { zig++; @@ -514,17 +548,17 @@ // Decode the DC coefficient, as specified in section F.2.2.1. byte value = decoder.DecodeHuffman( - ref decoder.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]); + ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].DcTableSelector]); if (value > 16) { throw new ImageFormatException("Excessive DC component"); } int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); - dc[compIndex] += deltaDC; + this.Pointers.Dc[compIndex] += deltaDC; // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al); + Block8x8F.SetScalarAt(b, 0, this.Pointers.Dc[compIndex] << al); } if (zig <= this.zigEnd && decoder.EobRun > 0) @@ -534,7 +568,6 @@ else { // Decode the AC coefficients, as specified in section F.2.2.2. - // Huffman huffv = ; for (; zig <= this.zigEnd; zig++) { byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]); @@ -578,8 +611,8 @@ if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0) { // We haven't completely decoded this 8x8 block. Save the coefficients. - // this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone(); - decoder.ProgCoeffs[compIndex][((this.by * this.mxx) * hi) + this.bx] = *b; + // this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone(); + decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * hi) + this.bx] = *b; // At this point, we could execute the rest of the loop body to dequantize and // perform the inverse DCT, to save early stages of a progressive image to the diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index 22d4f1ca8..4b3b26da9 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -17,16 +17,6 @@ namespace ImageSharp.Formats /// internal unsafe class JpegDecoderCore : IDisposable { - /// - /// The AC table index - /// - internal const int AcTable = 1; - - /// - /// The DC table index - /// - internal const int DcTable = 0; - /// /// The maximum number of color components /// @@ -334,7 +324,7 @@ namespace ImageSharp.Formats return; } - this.ProcessStartOfScan22(remaining); + this.ProcessStartOfScan(remaining); break; case JpegConstants.Markers.DRI: if (configOnly) @@ -1090,164 +1080,6 @@ namespace ImageSharp.Formats } } - internal void ProcessBlockImpl( - int ah, - Block8x8F* b, - Block8x8F* temp1, - Block8x8F* temp2, - int* unzigPtr, - Scan[] scan, - int i, - int zigStart, - int zigEnd, - int al, - int[] dc, - int compIndex, - int @by, - int mxx, - int hi, - int bx, - Block8x8F* qt) - { - int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector; - if (ah != 0) - { - this.Refine(b, ref this.HuffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al); - } - else - { - int zig = zigStart; - if (zig == 0) - { - zig++; - - // Decode the DC coefficient, as specified in section F.2.2.1. - byte value = - this.DecodeHuffman( - ref this.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]); - if (value > 16) - { - throw new ImageFormatException("Excessive DC component"); - } - - int deltaDC = this.Bits.ReceiveExtend(value, this); - dc[compIndex] += deltaDC; - - // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al); - } - - if (zig <= zigEnd && this.EobRun > 0) - { - this.EobRun--; - } - else - { - // Decode the AC coefficients, as specified in section F.2.2.2. - // Huffman huffv = ; - for (; zig <= zigEnd; zig++) - { - byte value = this.DecodeHuffman(ref this.HuffmanTrees[huffmannIdx]); - byte val0 = (byte)(value >> 4); - byte val1 = (byte)(value & 0x0f); - if (val1 != 0) - { - zig += val0; - if (zig > zigEnd) - { - break; - } - - int ac = this.Bits.ReceiveExtend(val1, this); - - // b[Unzig[zig]] = ac << al; - Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al); - } - else - { - if (val0 != 0x0f) - { - this.EobRun = (ushort)(1 << val0); - if (val0 != 0) - { - this.EobRun |= (ushort)this.DecodeBits(val0); - } - - this.EobRun--; - break; - } - - zig += 0x0f; - } - } - } - } - - if (this.IsProgressive) - { - if (zigEnd != Block8x8F.ScalarCount - 1 || al != 0) - { - // We haven't completely decoded this 8x8 block. Save the coefficients. - - // TODO!!! - // throw new NotImplementedException(); - // this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone(); - this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = *b; - - // At this point, we could execute the rest of the loop body to dequantize and - // perform the inverse DCT, to save early stages of a progressive image to the - // *image.YCbCr buffers (the whole point of progressive encoding), but in Go, - // the jpeg.Decode function does not return until the entire image is decoded, - // so we "continue" here to avoid wasted computation. - return; - } - } - - // Dequantize, perform the inverse DCT and store the block to the image. - Block8x8F.UnZig(b, qt, unzigPtr); - - DCT.TransformIDCT(ref *b, ref *temp1, ref *temp2); - - var destChannel = this.GetDestinationChannel(compIndex); - var destArea = destChannel.GetOffsetedSubAreaForBlock(bx, by); - destArea.LoadColorsFrom(temp1, temp2); - } - - private void ProcessComponentImpl( - int i, - ref Scan currentScan, - Scan[] scan, - ref int totalHv, - ref Component 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 (currentScan.Index == scan[j].Index) - { - throw new ImageFormatException("Repeated component selector"); - } - } - - totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; - - currentScan.DcTableSelector = (byte)(this.Temp[2 + (2 * i)] >> 4); - if (currentScan.DcTableSelector > HuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad DC table selector value"); - } - - currentScan.AcTableSelector = (byte)(this.Temp[2 + (2 * i)] & 0x0f); - if (currentScan.AcTableSelector > HuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad AC table selector value"); - } - } - /// /// Processes a Define Huffman Table marker, and initializes a huffman /// struct from its contents. Specified in section B.2.4.2. @@ -1368,30 +1200,6 @@ namespace ImageSharp.Formats } } - private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv) - { - // Component selector. - int cs = this.Temp[1 + (2 * i)]; - int compIndex = -1; - for (int j = 0; j < this.ComponentCount; j++) - { - // Component compv = ; - if (cs == this.ComponentArray[j].Identifier) - { - compIndex = j; - } - } - - if (compIndex < 0) - { - throw new ImageFormatException("Unknown component selector"); - } - - currentScan.Index = (byte)compIndex; - - this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.ComponentArray[compIndex]); - } - /// /// Processes the Start of Frame marker. Specified in section B.2.2. /// @@ -1583,438 +1391,21 @@ namespace ImageSharp.Formats } } - private void ProcessStartOfScan22(int remaining) - { - Scan[] scan = new Scan[MaxComponents]; - int[] dc = new int[MaxComponents]; - - DecoderScanProcessor p = default(DecoderScanProcessor); - DecoderScanProcessor.Init(&p, this, remaining, scan); - this.Bits = default(Bits); - this.MakeImage(p.mxx, p.myy); - p.ProcessBlocks(this, scan, ref dc); - - } - /// - /// Processes the SOS (Start of scan marker). + /// Processes the SOS (Start of scan marker). /// - /// - /// TODO: This also needs some significant refactoring to follow a more OO format. - /// /// The remaining bytes in the segment block. /// - /// Missing SOF Marker - /// SOS has wrong length + /// Missing SOF Marker + /// SOS has wrong length /// private void ProcessStartOfScan(int remaining) { - if (this.ComponentCount == 0) - { - throw new ImageFormatException("Missing SOF marker"); - } - - if (remaining < 6 || 4 + (2 * this.ComponentCount) < remaining || remaining % 2 != 0) - { - throw new ImageFormatException("SOS has wrong length"); - } - - this.ReadFull(this.Temp, 0, remaining); - byte scanComponentCount = this.Temp[0]; - - int scanComponentCountX2 = 2 * scanComponentCount; - if (remaining != 4 + scanComponentCountX2) - { - throw new ImageFormatException("SOS length inconsistent with number of components"); - } - - Scan[] scan = new Scan[MaxComponents]; - int totalHv = 0; - - for (int i = 0; i < scanComponentCount; i++) - { - this.ProcessScanImpl(i, ref scan[i], scan, 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 (this.ComponentCount > 1 && totalHv > 10) - { - throw new ImageFormatException("Total sampling factors too large."); - } - - // zigStart and zigEnd are the spectral selection bounds. - // ah and al 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. - int zigStart = 0; - int zigEnd = Block8x8F.ScalarCount - 1; - int ah = 0; - int al = 0; - - if (this.IsProgressive) - { - zigStart = this.Temp[1 + scanComponentCountX2]; - zigEnd = this.Temp[2 + scanComponentCountX2]; - ah = this.Temp[3 + scanComponentCountX2] >> 4; - al = this.Temp[3 + scanComponentCountX2] & 0x0f; - - if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || zigEnd >= Block8x8F.ScalarCount) - { - throw new ImageFormatException("Bad spectral selection bounds"); - } - - if (zigStart != 0 && scanComponentCount != 1) - { - throw new ImageFormatException("Progressive AC coefficients for more than one component"); - } - - if (ah != 0 && ah != al + 1) - { - throw new ImageFormatException("Bad successive approximation values"); - } - } - - // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; - int mxx = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); - int myy = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); - - if (this.IsProgressive) - { - for (int i = 0; i < scanComponentCount; i++) - { - int compIndex = scan[i].Index; - if (this.ProgCoeffs[compIndex] == null) - { - int size = mxx * myy * this.ComponentArray[compIndex].HorizontalFactor - * this.ComponentArray[compIndex].VerticalFactor; - - this.ProgCoeffs[compIndex] = new Block8x8F[size]; - } - } - } - + JpegScanDecoder scan = default(JpegScanDecoder); + JpegScanDecoder.Init(&scan, this, remaining); this.Bits = default(Bits); - - int mcu = 0; - byte expectedRst = JpegConstants.Markers.RST0; - - // b is the decoded coefficients block, in natural (not zig-zag) order. - // Block b; - int[] dc = new int[MaxComponents]; - - // bx and by are the location of the current block, in units of 8x8 - // blocks: the third block in the first row has (bx, by) = (2, 0). - int bx, by, blockCount = 0; - - // TODO: A DecoderScanProcessor struct could clean up this mess - Block8x8F b = default(Block8x8F); - Block8x8F temp1 = default(Block8x8F); - Block8x8F temp2 = default(Block8x8F); - - UnzigData unzig = UnzigData.Create(); - - int* unzigPtr = unzig.Data; - - - this.MakeImage(mxx, myy); - - - for (int my = 0; my < myy; my++) - { - for (int mx = 0; mx < mxx; mx++) - { - for (int i = 0; i < scanComponentCount; i++) - { - int compIndex = scan[i].Index; - int hi = this.ComponentArray[compIndex].HorizontalFactor; - int vi = this.ComponentArray[compIndex].VerticalFactor; - - for (int j = 0; j < hi * vi; j++) - { - // 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 - if (scanComponentCount != 1) - { - bx = (hi * mx) + (j % hi); - by = (vi * my) + (j / hi); - } - else - { - int q = mxx * hi; - bx = blockCount % q; - by = blockCount / q; - blockCount++; - if (bx * 8 >= this.ImageWidth || by * 8 >= this.ImageHeight) - { - continue; - } - } - - int qtIndex = this.ComponentArray[compIndex].Selector; - - // TODO: A DecoderScanProcessor struct could clean up this mess - // TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async. - fixed (Block8x8F* qtp = &this.QuantizationTables[qtIndex]) - { - // Load the previous partially decoded coefficients, if applicable. - if (this.IsProgressive) - { - int blockIndex = ((@by * mxx) * hi) + bx; - - fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][blockIndex]) - { - Unsafe.CopyBlock(&b, bp, (uint) sizeof(Block8x8F) ); - } - } - else - { - b.Clear(); - } - - this.ProcessBlockImpl( - ah, - &b, - &temp1, - &temp2, - unzigPtr, - scan, - i, - zigStart, - zigEnd, - al, - dc, - compIndex, - @by, - mxx, - hi, - bx, - qtp); - } - } - - // for j - } - - // for i - mcu++; - - if (this.RestartInterval > 0 && mcu % this.RestartInterval == 0 && mcu < mxx * myy) - { - // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, - // but this one assumes well-formed input, and hence the restart marker follows immediately. - this.ReadFull(this.Temp, 0, 2); - if (this.Temp[0] != 0xff || this.Temp[1] != expectedRst) - { - throw new ImageFormatException("Bad RST marker"); - } - - expectedRst++; - if (expectedRst == JpegConstants.Markers.RST7 + 1) - { - expectedRst = JpegConstants.Markers.RST0; - } - - // Reset the Huffman decoder. - this.Bits = default(Bits); - - // Reset the DC components, as per section F.2.1.3.1. - dc = new int[MaxComponents]; - - // Reset the progressive decoder state, as per section G.1.2.2. - this.EobRun = 0; - } - } - - // for mx - } - - // for my - } - - /// - /// Decodes a successive approximation refinement block, as specified in section G.1.2. - /// - /// The block of coefficients - /// The Huffman tree - /// Unzig ptr - /// The zig-zag start index - /// The zig-zag end index - /// The low transform offset - private void Refine(Block8x8F* b, ref HuffmanTree h, int* unzigPtr, int zigStart, int zigEnd, int delta) - { - // Refining a DC component is trivial. - if (zigStart == 0) - { - if (zigEnd != 0) - { - throw new ImageFormatException("Invalid state for zig DC component"); - } - - bool bit = this.DecodeBit(); - if (bit) - { - int stuff = (int)Block8x8F.GetScalarAt(b, 0); - - // int stuff = (int)b[0]; - stuff |= delta; - - // b[0] = stuff; - Block8x8F.SetScalarAt(b, 0, stuff); - } - - return; - } - - // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. - int zig = zigStart; - if (this.EobRun == 0) - { - for (; zig <= zigEnd; zig++) - { - bool done = false; - int z = 0; - byte val = this.DecodeHuffman(ref h); - int val0 = val >> 4; - int val1 = val & 0x0f; - - switch (val1) - { - case 0: - if (val0 != 0x0f) - { - this.EobRun = (ushort)(1 << val0); - if (val0 != 0) - { - this.EobRun |= (ushort)this.DecodeBits(val0); - } - - done = true; - } - - break; - case 1: - z = delta; - bool bit = this.DecodeBit(); - if (!bit) - { - z = -z; - } - - break; - default: - throw new ImageFormatException("Unexpected Huffman code"); - } - - if (done) - { - break; - } - - int blah = zig; - - zig = this.RefineNonZeroes(b, zig, zigEnd, val0, delta, unzigPtr); - if (zig > zigEnd) - { - throw new ImageFormatException($"Too many coefficients {zig} > {zigEnd}"); - } - - if (z != 0) - { - // b[Unzig[zig]] = z; - Block8x8F.SetScalarAt(b, unzigPtr[zig], z); - } - } - } - - if (this.EobRun > 0) - { - this.EobRun--; - this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr); - } - } - - /// - /// Refines non-zero entries of b in zig-zag order. - /// If >= 0, the first zero entries are skipped over. - /// - /// The block of coefficients - /// The zig-zag start index - /// The zig-zag end index - /// The non-zero entry - /// The low transform offset - /// Pointer to the Jpeg Unzig data (data part of ) - /// The - private int RefineNonZeroes(Block8x8F* b, int zig, int zigEnd, int nz, int delta, int* unzigPtr) - { - for (; zig <= zigEnd; zig++) - { - int u = unzigPtr[zig]; - float bu = Block8x8F.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; - } - - bool bit = this.DecodeBit(); - if (!bit) - { - continue; - } - - if (bu >= 0) - { - // b[u] += delta; - Block8x8F.SetScalarAt(b, u, bu + delta); - } - else - { - // b[u] -= delta; - Block8x8F.SetScalarAt(b, u, bu - delta); - } - } - - return zig; + this.MakeImage(scan.XNumberOfMCUs, scan.YNumberOfMCUs); + scan.ProcessBlocks(this); } ///