/// <summary> /// The implementation part of <see cref="Init"/> as an instance method. /// </summary> /// <param name="decoder">The <see cref="JpegDecoderCore"/></param> /// <param name="remaining">The remaining bytes</param> private void InitImpl(JpegDecoderCore 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.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.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. if (decoder.ComponentCount > 1 && totalHv > 10) { throw new ImageFormatException("Total sampling factors too large."); } this.zigEnd = Block8x8F.ScalarCount - 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.ScalarCount) { 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"); } } // 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.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.componentScanCount; i++) { int compIndex = this.pointers.Scan[i].Index; if (decoder.ProgCoeffs[compIndex] == null) { int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor * decoder.ComponentArray[compIndex].VerticalFactor; decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; } } } }
/// <summary> /// Internal part of the DHT processor, whatever does it mean /// </summary> /// <param name="decoder">The decoder instance</param> /// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param> /// <param name="remaining">Remaining bits</param> public void ProcessDefineHuffmanTablesMarkerLoop( JpegDecoderCore decoder, 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"); } decoder.ReadFull(this.Values, 0, this.Length); // Derive the look-up table. for (int i = 0; i < this.Lut.Length; i++) { this.Lut[i] = 0; } uint x = 0, code = 0; for (int i = 0; i < LutSize; 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. byte base2 = (byte)(code << (7 - i)); ushort lutValue = (ushort)((this.Values[x] << 8) | (2 + i)); for (int k = 0; k < 1 << (7 - i); k++) { this.Lut[base2 | k] = lutValue; } code++; x++; } } // 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) { this.MinCodes[i] = -1; this.MaxCodes[i] = -1; this.Indices[i] = -1; } else { this.MinCodes[i] = c; this.MaxCodes[i] = c + nc - 1; this.Indices[i] = index; c += nc; index += nc; } c <<= 1; } }
/// <summary> /// Reads the blocks from the <see cref="JpegDecoderCore"/>-s stream, and processes them into the corresponding <see cref="JpegPixelArea"/> instances. /// </summary> /// <param name="decoder">The <see cref="JpegDecoderCore"/> instance</param> public void ProcessBlocks(JpegDecoderCore decoder) { int blockCount = 0; int mcu = 0; byte expectedRst = JpegConstants.Markers.RST0; for (int my = 0; my < this.YNumberOfMCUs; my++) { for (int mx = 0; mx < this.XNumberOfMCUs; mx++) { for (int i = 0; i < this.componentScanCount; i++) { int compIndex = this.pointers.Scan[i].Index; int hi = decoder.ComponentArray[compIndex].HorizontalFactor; int vi = decoder.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 (this.componentScanCount != 1) { this.bx = (hi * mx) + (j % hi); this.by = (vi * my) + (j / hi); } else { int q = this.XNumberOfMCUs * hi; this.bx = blockCount % q; this.by = blockCount / q; blockCount++; if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) { continue; } } int qtIndex = decoder.ComponentArray[compIndex].Selector; // TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async. this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; // Load the previous partially decoded coefficients, if applicable. if (decoder.IsProgressive) { int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx; this.data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; } else { this.data.Block.Clear(); } this.ProcessBlockImpl(decoder, i, compIndex, hi); } // for j } // for i mcu++; 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. decoder.ReadFull(decoder.Temp, 0, 2); if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) { throw new ImageFormatException("Bad RST marker"); } expectedRst++; if (expectedRst == JpegConstants.Markers.RST7 + 1) { expectedRst = JpegConstants.Markers.RST0; } // Reset the Huffman decoder. decoder.Bits = default(Bits); // Reset the DC components, as per section F.2.1.3.1. this.ResetDc(); // Reset the progressive decoder state, as per section G.1.2.2. this.eobRun = 0; } } // for mx } }