From 9cb5e0521c47133238f97a9e8f6b4305750c5bba Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 29 Dec 2016 00:47:11 +0100 Subject: [PATCH] JpegScanDecoder docs --- .../Jpg/Components/Decoder/JpegScanDecoder.cs | 293 ++++++++++-------- .../Jpg/Components/Decoder/JpegScanDecoder.md | 8 +- 2 files changed, 166 insertions(+), 135 deletions(-) diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs index 34e51eaf03..d12f00ada4 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Formats.Jpg /// Holds the "large" data blocks needed for computations /// [StructLayout(LayoutKind.Sequential)] - public struct ComponentData + public struct ComputationData { /// /// The main input block @@ -58,42 +58,63 @@ namespace ImageSharp.Formats.Jpg public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents]; /// - /// The DC data + /// The DC component values /// public fixed int Dc[JpegDecoderCore.MaxComponents]; /// - /// Creates and initializes a new instance + /// Creates and initializes a new instance /// /// - public static ComponentData Create() + public static ComputationData Create() { - ComponentData data = default(ComponentData); + ComputationData data = default(ComputationData); data.Unzig = UnzigData.Create(); return data; } } /// - /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of + /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of /// - public struct ComponentPointers + public struct DataPointers { + /// + /// Pointer to + /// public Block8x8F* Block; + /// + /// Pointer to + /// public Block8x8F* Temp1; + /// + /// Pointer to + /// public Block8x8F* Temp2; + /// + /// Pointer to + /// public Block8x8F* QuantiazationTable; + /// + /// Pointer to as int* + /// public int* Unzig; + /// + /// Pointer to as Scan* + /// public Scan* Scan; + /// + /// Pointer to + /// public int* Dc; - public ComponentPointers(ComponentData* basePtr) + public DataPointers(ComputationData* basePtr) { this.Block = &basePtr->Block; this.Temp1 = &basePtr->Temp1; @@ -171,35 +192,38 @@ namespace ImageSharp.Formats.Jpg /// public int YNumberOfMCUs; - private int scanComponentCount; + /// + /// The number of component scans + /// + private int componentScanCount; /// - /// The buffer + /// The buffer /// - private ComponentData Data; + private ComputationData Data; /// /// Pointers to elements of /// - private ComponentPointers Pointers; + private DataPointers Pointers; /// - /// Initializes the default instance after creation + /// Initializes the default instance after creation. /// - /// - /// - /// + /// Pointer to on the stack + /// The instance + /// The remaining bytes in the segment block. public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) { - p->Data = ComponentData.Create(); - p->Pointers = new ComponentPointers(&p->Data); + p->Data = ComputationData.Create(); + p->Pointers = new DataPointers(&p->Data); p->InitImpl(decoder, remaining); } /// /// Reads the blocks from the -s stream, and processes them into the corresponding instances. /// - /// + /// The instance public void ProcessBlocks(JpegDecoderCore decoder) { int blockCount = 0; @@ -210,7 +234,7 @@ namespace ImageSharp.Formats.Jpg { for (int mx = 0; mx < this.XNumberOfMCUs; mx++) { - for (int i = 0; i < this.scanComponentCount; i++) + for (int i = 0; i < this.componentScanCount; i++) { int compIndex = this.Pointers.Scan[i].Index; int hi = decoder.ComponentArray[compIndex].HorizontalFactor; @@ -240,7 +264,7 @@ namespace ImageSharp.Formats.Jpg // The non-interleaved scans will process only 6 Y blocks: // 0 1 2 // 3 4 5 - if (this.scanComponentCount != 1) + if (this.componentScanCount != 1) { this.bx = (hi * mx) + (j % hi); this.by = (vi * my) + (j / hi); @@ -332,9 +356,9 @@ namespace ImageSharp.Formats.Jpg } decoder.ReadFull(decoder.Temp, 0, remaining); - this.scanComponentCount = decoder.Temp[0]; + this.componentScanCount = decoder.Temp[0]; - int scanComponentCountX2 = 2 * this.scanComponentCount; + int scanComponentCountX2 = 2 * this.componentScanCount; if (remaining != 4 + scanComponentCountX2) { throw new ImageFormatException("SOS length inconsistent with number of components"); @@ -342,10 +366,11 @@ namespace ImageSharp.Formats.Jpg int totalHv = 0; - for (int i = 0; i < this.scanComponentCount; i++) + 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) @@ -368,7 +393,7 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Bad spectral selection bounds"); } - if (this.zigStart != 0 && this.scanComponentCount != 1) + if (this.zigStart != 0 && this.componentScanCount != 1) { throw new ImageFormatException("Progressive AC coefficients for more than one component"); } @@ -387,7 +412,7 @@ namespace ImageSharp.Formats.Jpg if (decoder.IsProgressive) { - for (int i = 0; i < this.scanComponentCount; i++) + for (int i = 0; i < this.componentScanCount; i++) { int compIndex = this.Pointers.Scan[i].Index; if (decoder.ProgCoeffs[compIndex] == null) @@ -401,6 +426,117 @@ namespace ImageSharp.Formats.Jpg } } + /// + /// Process the current block at (, ) + /// + /// The decoder + /// The index of the scan + /// The component index + /// Horizontal sampling factor at the given component index + private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi) + { + var b = this.Pointers.Block; + //var dc = this.Pointers.Dc; + 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 = this.zigStart; + if (zig == 0) + { + zig++; + + // Decode the DC coefficient, as specified in section F.2.2.1. + byte value = + decoder.DecodeHuffman( + 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); + this.Pointers.Dc[compIndex] += deltaDC; + + // b[0] = dc[compIndex] << al; + Block8x8F.SetScalarAt(b, 0, this.Pointers.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. + 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 * 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 + // *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); + } + private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv) { // Component selector. @@ -608,110 +744,5 @@ namespace ImageSharp.Formats.Jpg return zig; } - - private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi) - { - var b = this.Pointers.Block; - //var dc = this.Pointers.Dc; - 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 = this.zigStart; - if (zig == 0) - { - zig++; - - // Decode the DC coefficient, as specified in section F.2.2.1. - byte value = - decoder.DecodeHuffman( - 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); - this.Pointers.Dc[compIndex] += deltaDC; - - // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, this.Pointers.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. - 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 * 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 - // *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); - } } - } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md index 09e3c80bb8..215f21807b 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md @@ -16,10 +16,10 @@ are members of the `JpegScanDecoder` struct |JpegScanDecoder | |-------------------| |Variables | -|ComponentData | -|ComponentPointers | +|ComputationData | +|DataPointers | -- **ComponentData** holds the "large" data blocks needed for computations (Mostly `Block8x8F`-s) -- **ComponentPointers** contains pointers to the memory regions of `ComponentData` so they can be easily passed around to pointer based utility methods of `Block8x8F` +- **ComputationData** holds the "large" data blocks needed for computations (Mostly `Block8x8F`-s) +- **DataPointers** contains pointers to the memory regions of `ComponentData` so they can be easily passed around to pointer based utility methods of `Block8x8F`