// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // // ReSharper disable InconsistentNaming namespace ImageSharp.Formats.Jpg { using System; using System.Runtime.CompilerServices; /// /// Encapsulates the impementation of Jpeg SOS decoder. 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. /// internal unsafe partial struct JpegScanDecoder { /// /// The AC table index /// public const int AcTableIndex = 1; /// /// The DC table index /// public const int DcTableIndex = 0; /// /// 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 ushort eobRun; /// /// Pointers to elements of /// private DataPointers pointers; /// /// The buffer /// private ComputationData data; /// /// 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(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) { Init(p); p->InitStreamReadingImpl(decoder, remaining); } /// /// Initializes a default-constructed instance, filling the data and setting the pointers. /// /// Pointer to on the stack public static void Init(JpegScanDecoder* p) { p->data = ComputationData.Create(); p->pointers = new DataPointers(&p->data); } /// /// Loads the data from the given into the block. /// /// The public void LoadMemento(ref DecodedBlockMemento memento) { this.bx = memento.Bx; this.by = memento.By; this.data.Block = memento.Block; } /// /// Reads the blocks from the -s stream, and processes them into the corresponding instances. /// 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 ReadBlocks(JpegDecoderCore decoder) { int blockCount = 0; int mcu = 0; byte expectedRst = JpegConstants.Markers.RST0; for (int my = 0; my < decoder.MCUCountY; my++) { for (int mx = 0; mx < decoder.MCUCountX; mx++) { 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; 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 = blockCount % q; this.by = blockCount / q; blockCount++; if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) { continue; } } this.ReadBlock(decoder, scanIndex); } // for j } // for i mcu++; if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount) { // 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 } } /// /// Dequantize, perform the inverse DCT and store the block to the into the corresponding instances. /// /// The instance public void ProcessBlock(JpegDecoderCore decoder) { int qtIndex = decoder.ComponentArray[this.ComponentIndex].Selector; this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; Block8x8F* b = this.pointers.Block; Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); var destChannel = decoder.GetDestinationChannel(this.ComponentIndex); var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); } private void ResetDc() { Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); } /// /// The implementation part of as an instance method. /// /// The /// The remaining bytes private void InitStreamReadingImpl(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.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.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"); } } } /// /// Read the current the current block at (, ) from the decoders stream /// /// The decoder /// The index of the scan private void ReadBlock(JpegDecoderCore decoder, int scanIndex) { int blockIndex = this.GetBlockIndex(decoder); this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block; var b = this.pointers.Block; DecoderErrorCode errorCode; int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { this.Refine(decoder, 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. byte value; int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; errorCode = decoder.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], out value); errorCode.EnsureNoEOF(); if (value > 16) { throw new ImageFormatException("Excessive DC component"); } int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); this.pointers.Dc[this.ComponentIndex] += deltaDC; // b[0] = dc[compIndex] << al; Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.ComponentIndex] << this.al); } 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++) { byte value; errorCode = decoder.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); errorCode.EnsureNoEOF(); byte val0 = (byte)(value >> 4); byte val1 = (byte)(value & 0x0f); if (val1 != 0) { zig += val0; if (zig > this.zigEnd) { break; } int ac = decoder.Bits.ReceiveExtend(val1, decoder); // b[Unzig[zig]] = ac << al; Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); } else { if (val0 != 0x0f) { this.eobRun = (ushort)(1 << val0); if (val0 != 0) { errorCode = this.DecodeEobRun(val0, decoder); errorCode.EnsureNoError(); } this.eobRun--; break; } zig += 0x0f; } } } } DecodedBlockMemento[] blocks = decoder.DecodedBlocks[this.ComponentIndex]; DecodedBlockMemento.Store(blocks, blockIndex, this.bx, this.by, ref *b); } private DecoderErrorCode DecodeEobRun(int count, JpegDecoderCore decoder) { uint bitsResult; DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); if (errorCode != DecoderErrorCode.NoError) { return errorCode; } this.eobRun |= (ushort)bitsResult; return DecoderErrorCode.NoError; } /// /// Gets the block index used to retieve blocks from in /// /// The instance /// The index private int GetBlockIndex(JpegDecoderCore decoder) { return ((this.by * decoder.MCUCountX) * this.hi) + this.bx; } private void InitComponentScan(JpegDecoderCore decoder, int i, ref ComponentScan 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.ComponentArray[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, ref decoder.ComponentArray[compIndex]); } private void ProcessComponentImpl( JpegDecoderCore decoder, int i, ref ComponentScan currentComponentScan, 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 (currentComponentScan.ComponentIndex == this.pointers.ComponentScan[j].ComponentIndex) { throw new ImageFormatException("Repeated component selector"); } } totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); if (currentComponentScan.DcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); if (currentComponentScan.AcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad AC table selector value"); } } /// /// Decodes a successive approximation refinement block, as specified in section G.1.2. /// /// The decoder instance /// The Huffman tree /// The low transform offset private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta) { Block8x8F* 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"); } bool bit; DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit); errorCode.EnsureNoError(); 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 = this.zigStart; if (this.eobRun == 0) { for (; zig <= this.zigEnd; zig++) { bool done = false; int z = 0; byte val; DecoderErrorCode errorCode = decoder.DecodeHuffmanUnsafe(ref h, out val); errorCode.EnsureNoEOF(); int val0 = val >> 4; int val1 = val & 0x0f; switch (val1) { case 0: if (val0 != 0x0f) { this.eobRun = (ushort)(1 << val0); if (val0 != 0) { errorCode = this.DecodeEobRun(val0, decoder); errorCode.EnsureNoError(); } done = true; } break; case 1: z = delta; bool bit; errorCode = decoder.DecodeBitUnsafe(out bit); errorCode.EnsureNoError(); if (!bit) { z = -z; } break; default: throw new ImageFormatException("Unexpected Huffman code"); } if (done) { break; } zig = this.RefineNonZeroes(decoder, zig, val0, delta); if (zig > this.zigEnd) { throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); } if (z != 0) { // b[Unzig[zig]] = z; Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z); } } } if (this.eobRun > 0) { this.eobRun--; 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 <= this.zigEnd; zig++) { int u = this.pointers.Unzig[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; DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit); errorCode.EnsureNoError(); 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; } } }