From b98638ff9854390504ceea2ee29cfceedd1742cd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 22 Jan 2017 02:47:30 +0100 Subject: [PATCH] further Jpeg cleanup & refactor --- .../Decoder/{Scan.cs => ComponentScan.cs} | 6 +- .../Components/Decoder/JpegScanDecoder.cs | 94 +++++++++---------- .../JpegDecoderCore.cs | 17 +++- 3 files changed, 61 insertions(+), 56 deletions(-) rename src/ImageSharp.Formats.Jpeg/Components/Decoder/{Scan.cs => ComponentScan.cs} (82%) diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Scan.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/ComponentScan.cs similarity index 82% rename from src/ImageSharp.Formats.Jpeg/Components/Decoder/Scan.cs rename to src/ImageSharp.Formats.Jpeg/Components/Decoder/ComponentScan.cs index 799c3cc31..89fc115ea 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Scan.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/ComponentScan.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -11,12 +11,12 @@ namespace ImageSharp.Formats.Jpg /// Represents a component scan /// [StructLayout(LayoutKind.Sequential)] - internal struct Scan + internal struct ComponentScan { /// /// Gets or sets the component index. /// - public byte Index; + public byte ComponentIndex; /// /// Gets or sets the DC table selector diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs index 44eb01a6d..0d588cdda 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -76,6 +76,16 @@ namespace ImageSharp.Formats.Jpg /// private int componentScanCount; + /// + /// The current component index + /// + private int componentIndex; + + /// + /// Horizontal sampling factor at the current component index + /// + private int hi; + /// /// End-of-Band run, specified in section G.1.2.2. /// @@ -120,11 +130,11 @@ namespace ImageSharp.Formats.Jpg { 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; + this.componentIndex = this.pointers.ComponentScan[i].ComponentIndex; + this.hi = decoder.ComponentArray[this.componentIndex].HorizontalFactor; + int vi = decoder.ComponentArray[this.componentIndex].VerticalFactor; - for (int j = 0; j < hi * vi; j++) + for (int j = 0; j < this.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. @@ -150,12 +160,12 @@ namespace ImageSharp.Formats.Jpg // 3 4 5 if (this.componentScanCount != 1) { - this.bx = (hi * mx) + (j % hi); - this.by = (vi * my) + (j / hi); + this.bx = (this.hi * mx) + (j % this.hi); + this.by = (vi * my) + (j / this.hi); } else { - int q = decoder.MCUCountX * hi; + int q = decoder.MCUCountX * this.hi; this.bx = blockCount % q; this.by = blockCount / q; blockCount++; @@ -165,7 +175,7 @@ namespace ImageSharp.Formats.Jpg } } - int qtIndex = decoder.ComponentArray[compIndex].Selector; + int qtIndex = decoder.ComponentArray[this.componentIndex].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]; @@ -173,15 +183,15 @@ namespace ImageSharp.Formats.Jpg // Load the previous partially decoded coefficients, if applicable. if (decoder.IsProgressive) { - int blockIndex = ((this.by * decoder.MCUCountX) * hi) + this.bx; - this.data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; + int blockIndex = this.GetBlockIndex(decoder); + this.data.Block = decoder.DecodedBlocks[this.componentIndex][blockIndex]; } else { this.data.Block.Clear(); } - this.ProcessBlockImpl(decoder, i, compIndex, hi); + this.ProcessBlockImpl(decoder, i); } // for j @@ -256,7 +266,7 @@ namespace ImageSharp.Formats.Jpg for (int i = 0; i < this.componentScanCount; i++) { - this.ProcessScanImpl(decoder, i, ref this.pointers.Scan[i], ref totalHv); + this.ProcessScanImpl(decoder, i, ref this.pointers.ComponentScan[i], ref totalHv); } // Section B.2.3 states that if there is more than one component then the @@ -291,22 +301,6 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Bad successive approximation values"); } } - - 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 = decoder.TotalMCUCount - * decoder.ComponentArray[compIndex].HorizontalFactor - * decoder.ComponentArray[compIndex].VerticalFactor; - - decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; - } - } - } } /// @@ -314,13 +308,11 @@ namespace ImageSharp.Formats.Jpg /// /// 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) + private void ProcessBlockImpl(JpegDecoderCore decoder, int i) { var b = this.pointers.Block; - int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].AcTableSelector; + int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[i].AcTableSelector; if (this.ah != 0) { this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); @@ -335,17 +327,17 @@ namespace ImageSharp.Formats.Jpg // 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]); + ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[i].DcTableSelector]); if (value > 16) { throw new ImageFormatException("Excessive DC component"); } int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); - this.pointers.Dc[compIndex] += deltaDC; + this.pointers.Dc[this.componentIndex] += deltaDC; // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[compIndex] << this.al); + Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.componentIndex] << this.al); } if (zig <= this.zigEnd && this.eobRun > 0) @@ -398,8 +390,7 @@ namespace ImageSharp.Formats.Jpg 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 * decoder.MCUCountX) * hi) + this.bx] = *b; + decoder.DecodedBlocks[this.componentIndex][this.GetBlockIndex(decoder)] = *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 @@ -415,12 +406,17 @@ namespace ImageSharp.Formats.Jpg DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); - var destChannel = decoder.GetDestinationChannel(compIndex); + var destChannel = decoder.GetDestinationChannel(this.componentIndex); 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) + private int GetBlockIndex(JpegDecoderCore decoder) + { + return ((this.@by * decoder.MCUCountX) * this.hi) + this.bx; + } + + private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref ComponentScan currentComponentScan, ref int totalHv) { // Component selector. int cs = decoder.Temp[1 + (2 * i)]; @@ -439,15 +435,15 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Unknown component selector"); } - currentScan.Index = (byte)compIndex; + currentComponentScan.ComponentIndex = (byte)compIndex; - this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); + this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); } private void ProcessComponentImpl( JpegDecoderCore decoder, int i, - ref Scan currentScan, + ref ComponentScan currentComponentScan, ref int totalHv, ref Component currentComponent) { @@ -458,7 +454,7 @@ namespace ImageSharp.Formats.Jpg // into comp are unique. for (int j = 0; j < i; j++) { - if (currentScan.Index == this.pointers.Scan[j].Index) + if (currentComponentScan.ComponentIndex == this.pointers.ComponentScan[j].ComponentIndex) { throw new ImageFormatException("Repeated component selector"); } @@ -466,14 +462,14 @@ namespace ImageSharp.Formats.Jpg totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; - currentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); - if (currentScan.DcTableSelector > HuffmanTree.MaxTh) + currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); + if (currentComponentScan.DcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } - currentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); - if (currentScan.AcTableSelector > HuffmanTree.MaxTh) + currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); + if (currentComponentScan.AcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad AC table selector value"); } @@ -714,7 +710,7 @@ namespace ImageSharp.Formats.Jpg /// /// Pointer to as Scan* /// - public Scan* Scan; + public ComponentScan* ComponentScan; /// /// Pointer to @@ -732,7 +728,7 @@ namespace ImageSharp.Formats.Jpg this.Temp2 = &basePtr->Temp2; this.QuantiazationTable = &basePtr->QuantiazationTable; this.Unzig = basePtr->Unzig.Data; - this.Scan = (Scan*)basePtr->ScanData; + this.ComponentScan = (ComponentScan*)basePtr->ScanData; this.Dc = basePtr->Dc; } } diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 451420ad5..707f9d3e4 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -88,7 +88,7 @@ namespace ImageSharp.Formats this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; this.ComponentArray = new Component[MaxComponents]; - this.ProgCoeffs = new Block8x8F[MaxComponents][]; + this.DecodedBlocks = new Block8x8F[MaxComponents][]; this.Bits = default(Bits); this.Bytes = Bytes.Create(); } @@ -107,6 +107,7 @@ namespace ImageSharp.Formats /// /// MissingFF00 /// + // ReSharper disable once InconsistentNaming MissingFF00 } @@ -122,8 +123,9 @@ namespace ImageSharp.Formats /// /// Gets the saved state between progressive-mode scans. + /// TODO: Also store non-progressive data here. (Helps splitting and parallelizing JpegScanDecoder-s loop) /// - public Block8x8F[][] ProgCoeffs { get; } + public Block8x8F[][] DecodedBlocks { get; } /// /// Gets the quantization tables, in zigzag order. @@ -131,9 +133,9 @@ namespace ImageSharp.Formats public Block8x8F[] QuantizationTables { get; } /// - /// Gets the temporary buffer for holding pixel (and other?) data + /// Gets the temporary buffer used to store bytes read from the stream. + /// TODO: Should be stack allocated, fixed sized buffer! /// - // TODO: the usage rules of this buffer seem to be unclean + need to consider stack-allocating it for perf public byte[] Temp { get; } /// @@ -1408,6 +1410,13 @@ namespace ImageSharp.Formats int v0 = this.ComponentArray[0].VerticalFactor; this.MCUCountX = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); this.MCUCountY = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); + + for (int i = 0; i < this.ComponentCount; i++) + { + int size = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor + * this.ComponentArray[i].VerticalFactor; + this.DecodedBlocks[i] = new Block8x8F[size]; + } } ///