diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs index 03013219c..390e5dd15 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs @@ -12,6 +12,16 @@ namespace ImageSharp.Formats.Jpg /// internal struct HuffmanTree : IDisposable { + /// + /// The index of the AC table row + /// + public const int AcTableIndex = 1; + + /// + /// The index of the DC table row + /// + public const int DcTableIndex = 0; + /// /// The maximum (inclusive) number of codes in a Huffman tree. /// diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs new file mode 100644 index 000000000..85018a06f --- /dev/null +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs @@ -0,0 +1,166 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg +{ + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + /// + /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct JpegBlockProcessor + { + /// + /// The + /// + private ComputationData data; + + /// + /// Pointers to elements of + /// + private DataPointers pointers; + + /// + /// The component index. + /// + private int componentIndex; + + /// + /// Initialize the instance on the stack. + /// + /// The instance + /// The current component index + public static void Init(JpegBlockProcessor* processor, int componentIndex) + { + processor->componentIndex = componentIndex; + processor->data = ComputationData.Create(); + processor->pointers = new DataPointers(&processor->data); + } + + /// + /// Dequantize, perform the inverse DCT and store the blocks to the into the corresponding instances. + /// + /// The instance + public void ProcessAllBlocks(JpegDecoderCore decoder) + { + DecodedBlockArray blockArray = decoder.DecodedBlocks[this.componentIndex]; + for (int i = 0; i < blockArray.Count; i++) + { + this.ProcessBlockColors(decoder, ref blockArray.Buffer[i]); + } + } + + /// + /// Dequantize, perform the inverse DCT and store decodedBlock.Block to the into the corresponding instance. + /// + /// The + /// The + private void ProcessBlockColors(JpegDecoderCore decoder, ref DecodedBlock decodedBlock) + { + this.data.Block = decodedBlock.Block; + 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(decodedBlock.Bx, decodedBlock.By); + destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); + } + + /// + /// Holds the "large" data blocks needed for computations. + /// + [StructLayout(LayoutKind.Sequential)] + public struct ComputationData + { + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F Block; + + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F Temp1; + + /// + /// Temporal block 2 to store intermediate and/or final computation results + /// + public Block8x8F Temp2; + + /// + /// The quantization table as + /// + public Block8x8F QuantiazationTable; + + /// + /// The jpeg unzig data + /// + public UnzigData Unzig; + + /// + /// Creates and initializes a new instance + /// + /// The + public static ComputationData Create() + { + 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 + /// + 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; + + /// + /// Initializes a new instance of the struct. + /// + /// Pointer to + internal DataPointers(ComputationData* dataPtr) + { + this.Block = &dataPtr->Block; + this.Temp1 = &dataPtr->Temp1; + this.Temp2 = &dataPtr->Temp2; + this.QuantiazationTable = &dataPtr->QuantiazationTable; + this.Unzig = dataPtr->Unzig.Data; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs index 06f170be5..7b910cdd2 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs @@ -23,21 +23,6 @@ namespace ImageSharp.Formats.Jpg /// public Block8x8F Block; - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Temp1; - - /// - /// Temporal block 2 to store intermediate and/or final computation results - /// - public Block8x8F Temp2; - - /// - /// The quantization table as - /// - public Block8x8F QuantiazationTable; - /// /// The jpeg unzig data /// diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs index b76ad59bb..52e25f3a8 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs @@ -20,21 +20,6 @@ namespace ImageSharp.Formats.Jpg /// public Block8x8F* Block; - /// - /// Pointer to - /// - public Block8x8F* Temp1; - - /// - /// Pointer to - /// - public Block8x8F* Temp2; - - /// - /// Pointer to - /// - public Block8x8F* QuantiazationTable; - /// /// Pointer to as int* /// @@ -57,9 +42,6 @@ namespace ImageSharp.Formats.Jpg public DataPointers(ComputationData* basePtr) { this.Block = &basePtr->Block; - this.Temp1 = &basePtr->Temp1; - this.Temp2 = &basePtr->Temp2; - this.QuantiazationTable = &basePtr->QuantiazationTable; this.Unzig = basePtr->Unzig.Data; this.ComponentScan = (ComponentScan*)basePtr->ScanData; this.Dc = basePtr->Dc; diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs index 103ee60c0..a43c545cd 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -7,10 +7,11 @@ namespace ImageSharp.Formats.Jpg { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; /// - /// Encapsulates the impementation of Jpeg SOS decoder. See JpegScanDecoder.md! - /// TODO: Split JpegScanDecoder: 1. JpegScanDecoder for Huffman-decoding () 2. JpegBlockProcessor for processing () + /// Encapsulates the impementation of Jpeg SOS Huffman decoding. 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. @@ -26,17 +27,21 @@ namespace ImageSharp.Formats.Jpg /// significant bit. /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. /// + [StructLayout(LayoutKind.Sequential)] internal unsafe partial struct JpegScanDecoder { + // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. +#pragma warning disable SA1202 // ElementsMustBeOrderedByAccess + /// - /// The AC table index + /// The buffer /// - public const int AcTableIndex = 1; + private ComputationData data; /// - /// The DC table index + /// Pointers to elements of /// - public const int DcTableIndex = 0; + private DataPointers pointers; /// /// The current component index @@ -88,16 +93,6 @@ namespace ImageSharp.Formats.Jpg /// private int eobRun; - /// - /// Pointers to elements of - /// - private DataPointers pointers; - - /// - /// The buffer - /// - private ComputationData data; - /// /// Initializes a default-constructed instance for reading data from -s stream. /// @@ -105,30 +100,10 @@ namespace ImageSharp.Formats.Jpg /// 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 DecodedBlock memento) - { - this.bx = memento.Bx; - this.by = memento.By; - this.data.Block = memento.Block; + p->InitStreamReadingImpl(decoder, remaining); } /// @@ -251,26 +226,6 @@ namespace ImageSharp.Formats.Jpg } } - /// - /// Dequantize, perform the inverse DCT and store the block to the into the corresponding instances. - /// - /// The instance - public void ProcessBlockColors(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); @@ -351,8 +306,7 @@ namespace ImageSharp.Formats.Jpg private void DecodeBlock(JpegDecoderCore decoder, int scanIndex) { var b = this.pointers.Block; - DecoderErrorCode errorCode; - int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; + int huffmannIdx = (HuffmanTree.AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); @@ -360,13 +314,14 @@ namespace ImageSharp.Formats.Jpg else { int zig = this.zigStart; + DecoderErrorCode errorCode; if (zig == 0) { zig++; // Decode the DC coefficient, as specified in section F.2.2.1. int value; - int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; + int huffmanIndex = (HuffmanTree.DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], out value); diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index fd06018a2..72655a500 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -102,9 +102,9 @@ namespace ImageSharp.Formats /// /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. - /// We need to apply IDCT and unzigging to transform them into color-space blocks. + /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. /// This is done by . - /// When ==true, we are touching these blocks each time we process a Scan. + /// When ==true, we are touching these blocks multiple times - each time we process a Scan. /// public DecodedBlockArray[] DecodedBlocks { get; } @@ -463,7 +463,7 @@ namespace ImageSharp.Formats /// /// Process the blocks in into Jpeg image channels ( and ) - /// are in a "raw" frequency-domain form. We need to apply IDCT and unzigging to transform them into color-space blocks. + /// are in a "raw" frequency-domain form. We need to apply IDCT, dequantization and unzigging to transform them into color-space blocks. /// We can copy these blocks into -s afterwards. /// /// The pixel type @@ -475,16 +475,9 @@ namespace ImageSharp.Formats this.ComponentCount, componentIndex => { - JpegScanDecoder scanDecoder = default(JpegScanDecoder); - JpegScanDecoder.Init(&scanDecoder); - - scanDecoder.ComponentIndex = componentIndex; - DecodedBlockArray blockArray = this.DecodedBlocks[componentIndex]; - for (int i = 0; i < blockArray.Count; i++) - { - scanDecoder.LoadMemento(ref blockArray.Buffer[i]); - scanDecoder.ProcessBlockColors(this); - } + JpegBlockProcessor processor = default(JpegBlockProcessor); + JpegBlockProcessor.Init(&processor, componentIndex); + processor.ProcessAllBlocks(this); }); }