diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs index 723ccd6b8..63d8b0e54 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs @@ -373,5 +373,12 @@ namespace ImageSharp.Formats.Jpg } } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(Block8x8F* dest, Block8x8F* source) + { + *dest = *source; + //Unsafe.CopyBlock(dest, source, (uint)sizeof(Block8x8F)); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs index b0c44c65b..c18b9cd99 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs @@ -1,9 +1,12 @@ namespace ImageSharp.Formats.Jpg { using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; internal unsafe struct DecoderScanProcessor { + [StructLayout(LayoutKind.Sequential)] public struct ComponentData { public Block8x8F Block; @@ -12,11 +15,13 @@ public Block8x8F Temp2; + public Block8x8F QuantiazationTable; + 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() { @@ -24,7 +29,6 @@ data.Unzig = UnzigData.Create(); return data; } - } public struct ComponentPointers @@ -35,23 +39,31 @@ public Block8x8F* Temp2; + public Block8x8F* QuantiazationTable; + public int* Unzig; - public Scan* Scan; + //public Scan* Scan; - public int* Dc; + //public int* Dc; public ComponentPointers(ComponentData* basePtr) { this.Block = &basePtr->Block; this.Temp1 = &basePtr->Temp1; 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); + //} + public int bx; public int by; @@ -68,17 +80,20 @@ public int myy; + public int scanComponentCount; + public ComponentData Data; public ComponentPointers Pointers; - public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining) + public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining, Scan[] scan) { + p->Data = ComponentData.Create(); p->Pointers = new ComponentPointers(&p->Data); - + p->InitImpl(decoder, remaining, scan); } - private void InitCommon(JpegDecoderCore decoder, int remaining) + private void InitImpl(JpegDecoderCore decoder, int remaining, Scan[] scan) { if (decoder.ComponentCount == 0) { @@ -91,9 +106,9 @@ } decoder.ReadFull(decoder.Temp, 0, remaining); - byte scanComponentCount = decoder.Temp[0]; + this.scanComponentCount = decoder.Temp[0]; - int scanComponentCountX2 = 2 * scanComponentCount; + int scanComponentCountX2 = 2 * this.scanComponentCount; if (remaining != 4 + scanComponentCountX2) { throw new ImageFormatException("SOS length inconsistent with number of components"); @@ -101,9 +116,9 @@ int totalHv = 0; - for (int i = 0; i < scanComponentCount; i++) + for (int i = 0; i < this.scanComponentCount; i++) { - this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv); + this.ProcessScanImpl(decoder, i, ref scan[i], ref totalHv, scan); } // 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. @@ -127,7 +142,7 @@ throw new ImageFormatException("Bad spectral selection bounds"); } - if (this.zigStart != 0 && scanComponentCount != 1) + if (this.zigStart != 0 && this.scanComponentCount != 1) { throw new ImageFormatException("Progressive AC coefficients for more than one component"); } @@ -146,12 +161,12 @@ if (decoder.IsProgressive) { - for (int i = 0; i < scanComponentCount; i++) + for (int i = 0; i < this.scanComponentCount; i++) { - int compIndex = this.Pointers.Scan[i].Index; + int compIndex = scan[i].Index; if (decoder.ProgCoeffs[compIndex] == null) { - int size = mxx * myy * decoder.ComponentArray[compIndex].HorizontalFactor + int size = this.mxx * this.myy * decoder.ComponentArray[compIndex].HorizontalFactor * decoder.ComponentArray[compIndex].VerticalFactor; decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; @@ -160,7 +175,7 @@ } } - private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv) + private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv, Scan[] scan) { // Component selector. int cs = decoder.Temp[1 + (2 * i)]; @@ -181,7 +196,7 @@ currentScan.Index = (byte)compIndex; - this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); + this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex], scan); } @@ -190,7 +205,7 @@ int i, ref Scan currentScan, ref int totalHv, - ref Component currentComponent) + ref Component currentComponent, Scan[] scan) { // 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 @@ -199,7 +214,7 @@ // into comp are unique. for (int j = 0; j < i; j++) { - if (currentScan.Index == this.Pointers.Scan[j].Index) + if (currentScan.Index == scan[j].Index) { throw new ImageFormatException("Repeated component selector"); } @@ -220,9 +235,391 @@ } } - private void InitProgressive(JpegDecoderCore decoder) + public void ProcessBlocks(JpegDecoderCore decoder, Scan[] scan, ref int[] dc) + { + int blockCount = 0; + int mcu = 0; + byte expectedRst = JpegConstants.Markers.RST0; + + for (int my = 0; my < this.myy; my++) + { + for (int mx = 0; mx < this.mxx; mx++) + { + for (int i = 0; i < this.scanComponentCount; i++) + { + int compIndex = 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.scanComponentCount != 1) + { + this.bx = (hi * mx) + (j % hi); + this.by = (vi * my) + (j / hi); + } + else + { + int q = this.mxx * 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.mxx) * hi) + this.bx; + this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; + } + else + { + this.Data.Block.Clear(); + } + + this.ProcessBlockImpl(decoder, i, compIndex, hi, scan, ref dc); + + + //fixed (Block8x8F* qtp = &decoder.QuantizationTables[qtIndex]) + //{ + // // Load the previous partially decoded coefficients, if applicable. + // if (decoder.IsProgressive) + // { + // int blockIndex = ((@by * mxx) * hi) + bx; + + // fixed (Block8x8F* bp = &decoder.ProgCoeffs[compIndex][blockIndex]) + // { + // Unsafe.CopyBlock(this.Pointers.Block, bp, (uint)sizeof(Block8x8F)); + // } + // } + // else + // { + // this.Data.Block.Clear(); + // } + // decoder.ProcessBlockImpl(this.ah, this.Pointers.Block, this.Pointers.Temp1, this.Pointers.Temp2, this.Pointers.Unzig, scan, i, this.zigStart, this.zigEnd, this.al, dc, compIndex, this.@by, this.mxx, hi, this.bx, qtp); + //} + + + } + + // for j + } + + // for i + mcu++; + + if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.mxx * this.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. + 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(); + dc = new int[JpegDecoderCore.MaxComponents]; + + // Reset the progressive decoder state, as per section G.1.2.2. + decoder.EobRun = 0; + } + } + + // for mx + } + } + + /// + /// The AC table index + /// + internal const int AcTable = 1; + + /// + /// The DC table index + /// + internal const int DcTable = 0; + + 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 = decoder.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 = this.zigStart; + if (decoder.EobRun == 0) + { + for (; zig <= this.zigEnd; zig++) + { + bool done = false; + int z = 0; + byte val = decoder.DecodeHuffman(ref h); + int val0 = val >> 4; + int val1 = val & 0x0f; + + switch (val1) + { + case 0: + if (val0 != 0x0f) + { + decoder.EobRun = (ushort)(1 << val0); + if (val0 != 0) + { + decoder.EobRun |= (ushort)decoder.DecodeBits(val0); + } + + done = true; + } + + break; + case 1: + z = delta; + bool bit = decoder.DecodeBit(); + if (!bit) + { + z = -z; + } + + break; + default: + throw new ImageFormatException("Unexpected Huffman code"); + } + + if (done) + { + break; + } + + int blah = zig; + + 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 (decoder.EobRun > 0) + { + decoder.EobRun--; + this.RefineNonZeroes(decoder, zig,-1, delta); + } + } + + private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta) + { + var b = this.Pointers.Block; + for (; zig <= 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 = decoder.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; + } + + private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi, Scan[] scan, ref int[] dc) { - + var b = this.Pointers.Block; + //var dc = this.Pointers.Dc; + int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector; + if (this.ah != 0) + { + this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); + } + else + { + int zig = zigStart; + if (zig == 0) + { + zig++; + + // 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]); + if (value > 16) + { + throw new ImageFormatException("Excessive DC component"); + } + + int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); + dc[compIndex] += deltaDC; + + // b[0] = dc[compIndex] << al; + Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al); + } + + if (zig <= this.zigEnd && decoder.EobRun > 0) + { + decoder.EobRun--; + } + 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]); + 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) + { + decoder.EobRun = (ushort)(1 << val0); + if (val0 != 0) + { + decoder.EobRun |= (ushort)decoder.DecodeBits(val0); + } + + decoder.EobRun--; + break; + } + + zig += 0x0f; + } + } + } + } + + if (decoder.IsProgressive) + { + 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; + + // 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, this.Pointers.QuantiazationTable, this.Pointers.Unzig); + + DCT.TransformIDCT(ref *b, ref *this.Pointers.Temp1, ref *this.Pointers.Temp2); + + var destChannel = decoder.GetDestinationChannel(compIndex); + var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); + destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2); } } diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index c17a2856e..7af8e534c 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Formats using System; using System.IO; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; using System.Threading.Tasks; using ImageSharp.Formats.Jpg; @@ -19,12 +20,12 @@ namespace ImageSharp.Formats /// /// The AC table index /// - private const int AcTable = 1; + internal const int AcTable = 1; /// /// The DC table index /// - private const int DcTable = 0; + internal const int DcTable = 0; /// /// The maximum number of color components @@ -44,7 +45,7 @@ namespace ImageSharp.Formats /// /// The huffman trees /// - private readonly HuffmanTree[] huffmanTrees; + internal HuffmanTree[] HuffmanTrees { get; } /// /// Saved state between progressive-mode scans. @@ -76,11 +77,11 @@ namespace ImageSharp.Formats /// /// Holds the unprocessed bits that have been taken from the byte-stream. /// - private Bits bits; + internal Bits Bits; private JpegPixelArea blackImage; - private int blockIndex; + //private int blockIndex; /// /// The byte buffer. @@ -95,7 +96,7 @@ namespace ImageSharp.Formats /// /// End-of-Band run, specified in section G.1.2.2. /// - private ushort eobRun; + internal ushort EobRun; /// /// A grayscale image to decode to. @@ -135,7 +136,7 @@ namespace ImageSharp.Formats /// /// The restart interval /// - private int restartInterval; + internal int RestartInterval { get; private set; } /// /// The vertical resolution. Calculated if the image has a JFIF header. @@ -152,15 +153,15 @@ namespace ImageSharp.Formats /// public JpegDecoderCore() { - this.huffmanTrees = HuffmanTree.CreateHuffmanTrees(); + this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees(); this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; this.ComponentArray = new Component[MaxComponents]; this.ProgCoeffs = new Block8x8F[MaxComponents][]; - this.bits = default(Bits); + this.Bits = default(Bits); this.bytes = Bytes.Create(); } - + /// /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent) /// It's better tho have an error code for this! @@ -333,7 +334,7 @@ namespace ImageSharp.Formats return; } - this.ProcessStartOfScan(remaining); + this.ProcessStartOfScan22(remaining); break; case JpegConstants.Markers.DRI: if (configOnly) @@ -430,9 +431,9 @@ namespace ImageSharp.Formats /// public void Dispose() { - for (int i = 0; i < this.huffmanTrees.Length; i++) + for (int i = 0; i < this.HuffmanTrees.Length; i++) { - this.huffmanTrees[i].Dispose(); + this.HuffmanTrees[i].Dispose(); } this.ycbcrImage?.Dispose(); @@ -440,7 +441,7 @@ namespace ImageSharp.Formats this.grayImage.ReturnPooled(); this.blackImage.ReturnPooled(); } - + /// /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. /// @@ -462,7 +463,7 @@ namespace ImageSharp.Formats // Unread the overshot bytes, if any. if (this.bytes.UnreadableBytes != 0) { - if (this.bits.UnreadBits >= 8) + if (this.Bits.UnreadBits >= 8) { this.UnreadByteStuffedByte(); } @@ -727,20 +728,20 @@ namespace ImageSharp.Formats /// Decodes a single bit /// /// The - private bool DecodeBit() + internal bool DecodeBit() { - if (this.bits.UnreadBits == 0) + if (this.Bits.UnreadBits == 0) { - ErrorCodes errorCode = this.bits.EnsureNBits(1, this); + ErrorCodes errorCode = this.Bits.EnsureNBits(1, this); if (errorCode != ErrorCodes.NoError) { throw new MissingFF00Exception(); } } - bool ret = (this.bits.Accumulator & this.bits.Mask) != 0; - this.bits.UnreadBits--; - this.bits.Mask >>= 1; + bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0; + this.Bits.UnreadBits--; + this.Bits.Mask >>= 1; return ret; } @@ -749,21 +750,21 @@ namespace ImageSharp.Formats /// /// The number of bits to decode. /// The - private uint DecodeBits(int count) + internal uint DecodeBits(int count) { - if (this.bits.UnreadBits < count) + if (this.Bits.UnreadBits < count) { - ErrorCodes errorCode = this.bits.EnsureNBits(count, this); + ErrorCodes errorCode = this.Bits.EnsureNBits(count, this); if (errorCode != ErrorCodes.NoError) { throw new MissingFF00Exception(); } } - uint ret = this.bits.Accumulator >> (this.bits.UnreadBits - count); + uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); ret = (uint)(ret & ((1 << count) - 1)); - this.bits.UnreadBits -= count; - this.bits.Mask >>= count; + this.Bits.UnreadBits -= count; + this.Bits.Mask >>= count; return ret; } @@ -772,7 +773,7 @@ namespace ImageSharp.Formats /// /// The huffman value /// The - private byte DecodeHuffman(ref HuffmanTree huffmanTree) + internal byte DecodeHuffman(ref HuffmanTree huffmanTree) { // Copy stuff to the stack: if (huffmanTree.Length == 0) @@ -780,20 +781,20 @@ namespace ImageSharp.Formats throw new ImageFormatException("Uninitialized Huffman table"); } - if (this.bits.UnreadBits < 8) + if (this.Bits.UnreadBits < 8) { - ErrorCodes errorCode = this.bits.EnsureNBits(8, this); + ErrorCodes errorCode = this.Bits.EnsureNBits(8, this); if (errorCode == ErrorCodes.NoError) { ushort v = - huffmanTree.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - HuffmanTree.LutSize)) & 0xff]; + huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff]; if (v != 0) { byte n = (byte)((v & 0xff) - 1); - this.bits.UnreadBits -= n; - this.bits.Mask >>= n; + this.Bits.UnreadBits -= n; + this.Bits.Mask >>= n; return (byte)(v >> 8); } } @@ -806,22 +807,22 @@ namespace ImageSharp.Formats int code = 0; for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) { - if (this.bits.UnreadBits == 0) + if (this.Bits.UnreadBits == 0) { - ErrorCodes errorCode = this.bits.EnsureNBits(1, this); + ErrorCodes errorCode = this.Bits.EnsureNBits(1, this); if (errorCode != ErrorCodes.NoError) { throw new MissingFF00Exception(); } } - if ((this.bits.Accumulator & this.bits.Mask) != 0) + if ((this.Bits.Accumulator & this.Bits.Mask) != 0) { code |= 1; } - this.bits.UnreadBits--; - this.bits.Mask >>= 1; + this.Bits.UnreadBits--; + this.Bits.Mask >>= 1; if (code <= huffmanTree.MaxCodes[i]) { @@ -834,7 +835,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Bad Huffman code"); } - private JpegPixelArea GetDestinationChannel(int compIndex) + internal JpegPixelArea GetDestinationChannel(int compIndex) { if (this.ComponentCount == 1) { @@ -1089,7 +1090,7 @@ namespace ImageSharp.Formats } } - private void ProcessBlockImpl( + internal void ProcessBlockImpl( int ah, Block8x8F* b, Block8x8F* temp1, @@ -1111,7 +1112,7 @@ namespace ImageSharp.Formats int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector; if (ah != 0) { - this.Refine(b, ref this.huffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al); + this.Refine(b, ref this.HuffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al); } else { @@ -1123,22 +1124,22 @@ namespace ImageSharp.Formats // 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]); + 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); + 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) + if (zig <= zigEnd && this.EobRun > 0) { - this.eobRun--; + this.EobRun--; } else { @@ -1146,7 +1147,7 @@ namespace ImageSharp.Formats // Huffman huffv = ; for (; zig <= zigEnd; zig++) { - byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]); + byte value = this.DecodeHuffman(ref this.HuffmanTrees[huffmannIdx]); byte val0 = (byte)(value >> 4); byte val1 = (byte)(value & 0x0f); if (val1 != 0) @@ -1157,7 +1158,7 @@ namespace ImageSharp.Formats break; } - int ac = this.bits.ReceiveExtend(val1, this); + int ac = this.Bits.ReceiveExtend(val1, this); // b[Unzig[zig]] = ac << al; Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al); @@ -1166,13 +1167,13 @@ namespace ImageSharp.Formats { if (val0 != 0x0f) { - this.eobRun = (ushort)(1 << val0); + this.EobRun = (ushort)(1 << val0); if (val0 != 0) { - this.eobRun |= (ushort)this.DecodeBits(val0); + this.EobRun |= (ushort)this.DecodeBits(val0); } - this.eobRun--; + this.EobRun--; break; } @@ -1276,7 +1277,7 @@ namespace ImageSharp.Formats } int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th; - this.huffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining); + this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining); } } @@ -1293,7 +1294,7 @@ namespace ImageSharp.Formats } this.ReadFull(this.Temp, 0, 2); - this.restartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1]; + this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1]; } /// @@ -1584,9 +1585,14 @@ namespace ImageSharp.Formats private void ProcessStartOfScan22(int remaining) { - DecoderScanProcessor processor = default(DecoderScanProcessor); - DecoderScanProcessor.Init(&processor, this, remaining); - this.MakeImage(processor.mxx, processor.myy); + Scan[] scan = new Scan[MaxComponents]; + int[] dc = new int[MaxComponents]; + + DecoderScanProcessor p = default(DecoderScanProcessor); + DecoderScanProcessor.Init(&p, this, remaining, scan); + this.MakeImage(p.mxx, p.myy); + p.ProcessBlocks(this, scan, ref dc); + } /// @@ -1699,7 +1705,7 @@ namespace ImageSharp.Formats } } - this.bits = default(Bits); + this.Bits = default(Bits); int mcu = 0; byte expectedRst = JpegConstants.Markers.RST0; @@ -1785,34 +1791,19 @@ namespace ImageSharp.Formats // Load the previous partially decoded coefficients, if applicable. if (this.IsProgressive) { - this.blockIndex = ((@by * mxx) * hi) + bx; - - fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][this.blockIndex]) + int blockIndex = ((@by * mxx) * hi) + bx; + + fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][blockIndex]) { - this.ProcessBlockImpl( - ah, - bp, - &temp1, - &temp2, - unzigPtr, - scan, - i, - zigStart, - zigEnd, - al, - dc, - compIndex, - @by, - mxx, - hi, - bx, - qtp); + Unsafe.CopyBlock(&b, bp, (uint) sizeof(Block8x8F) ); } } else { b.Clear(); - this.ProcessBlockImpl( + } + + this.ProcessBlockImpl( ah, &b, &temp1, @@ -1830,7 +1821,6 @@ namespace ImageSharp.Formats hi, bx, qtp); - } } } @@ -1840,7 +1830,7 @@ namespace ImageSharp.Formats // for i mcu++; - if (this.restartInterval > 0 && mcu % this.restartInterval == 0 && mcu < mxx * myy) + 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. @@ -1857,13 +1847,13 @@ namespace ImageSharp.Formats } // Reset the Huffman decoder. - this.bits = default(Bits); + 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; + this.EobRun = 0; } } @@ -1909,7 +1899,7 @@ namespace ImageSharp.Formats // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. int zig = zigStart; - if (this.eobRun == 0) + if (this.EobRun == 0) { for (; zig <= zigEnd; zig++) { @@ -1924,10 +1914,10 @@ namespace ImageSharp.Formats case 0: if (val0 != 0x0f) { - this.eobRun = (ushort)(1 << val0); + this.EobRun = (ushort)(1 << val0); if (val0 != 0) { - this.eobRun |= (ushort)this.DecodeBits(val0); + this.EobRun |= (ushort)this.DecodeBits(val0); } done = true; @@ -1968,9 +1958,9 @@ namespace ImageSharp.Formats } } - if (this.eobRun > 0) + if (this.EobRun > 0) { - this.eobRun--; + this.EobRun--; this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr); } } @@ -2035,7 +2025,7 @@ namespace ImageSharp.Formats // Unread the overshot bytes, if any. if (this.bytes.UnreadableBytes != 0) { - if (this.bits.UnreadBits >= 8) + if (this.Bits.UnreadBits >= 8) { this.UnreadByteStuffedByte(); } @@ -2073,11 +2063,11 @@ namespace ImageSharp.Formats { this.bytes.I -= this.bytes.UnreadableBytes; this.bytes.UnreadableBytes = 0; - if (this.bits.UnreadBits >= 8) + if (this.Bits.UnreadBits >= 8) { - this.bits.Accumulator >>= 8; - this.bits.UnreadBits -= 8; - this.bits.Mask >>= 8; + this.Bits.Accumulator >>= 8; + this.Bits.UnreadBits -= 8; + this.Bits.Mask >>= 8; } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 57b3c56f8..9bae8d9f3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -428,5 +428,21 @@ namespace ImageSharp.Tests Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); } + + [Fact] + public unsafe void Copy_FromHeap() + { + Block8x8F[] blox = new Block8x8F[1]; + + blox[0].LoadFrom(Create8x8FloatData()); + Block8x8F clone = default(Block8x8F); + + fixed (Block8x8F* p = &blox[0]) + { + Block8x8F.Copy(&clone, p); + } + + Assert.Equal(blox[0], clone); + } } } \ No newline at end of file