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);
});
}