Skip to content

Commit defec9c

Browse files
Merge branch 'master' into fixdsread
2 parents e3e2785 + ce2a28b commit defec9c

14 files changed

+262
-243
lines changed

src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,24 @@ internal class HuffmanScanDecoder
1818
{
1919
private readonly BufferedReadStream stream;
2020

21-
// Frame related
21+
/// <summary>
22+
/// <see cref="JpegFrame"/> instance containing decoding-related information.
23+
/// </summary>
2224
private JpegFrame frame;
25+
26+
/// <summary>
27+
/// Shortcut for <see cref="frame"/>.Components.
28+
/// </summary>
2329
private JpegComponent[] components;
2430

25-
// The restart interval.
31+
/// <summary>
32+
/// Number of component in the current scan.
33+
/// </summary>
34+
private int componentsCount;
35+
36+
/// <summary>
37+
/// The reset interval determined by RST markers.
38+
/// </summary>
2639
private int restartInterval;
2740

2841
// How many mcu's are left to do.
@@ -31,6 +44,16 @@ internal class HuffmanScanDecoder
3144
// The End-Of-Block countdown for ending the sequence prematurely when the remaining coefficients are zero.
3245
private int eobrun;
3346

47+
/// <summary>
48+
/// The DC Huffman tables.
49+
/// </summary>
50+
private readonly HuffmanTable[] dcHuffmanTables;
51+
52+
/// <summary>
53+
/// The AC Huffman tables
54+
/// </summary>
55+
private readonly HuffmanTable[] acHuffmanTables;
56+
3457
// The unzig data.
3558
private ZigZag dctZigZag;
3659

@@ -55,14 +78,16 @@ public HuffmanScanDecoder(
5578
this.stream = stream;
5679
this.spectralConverter = converter;
5780
this.cancellationToken = cancellationToken;
58-
}
5981

60-
// huffman tables
61-
public HuffmanTable[] DcHuffmanTables { get; set; }
62-
63-
public HuffmanTable[] AcHuffmanTables { get; set; }
82+
// TODO: this is actually a variable value depending on component count
83+
const int maxTables = 4;
84+
this.dcHuffmanTables = new HuffmanTable[maxTables];
85+
this.acHuffmanTables = new HuffmanTable[maxTables];
86+
}
6487

65-
// Reset interval
88+
/// <summary>
89+
/// Sets reset interval determined by RST markers.
90+
/// </summary>
6691
public int ResetInterval
6792
{
6893
set
@@ -72,9 +97,6 @@ public int ResetInterval
7297
}
7398
}
7499

75-
// The number of interleaved components.
76-
public int ComponentsLength { get; set; }
77-
78100
// The spectral selection start.
79101
public int SpectralStart { get; set; }
80102

@@ -90,10 +112,12 @@ public int ResetInterval
90112
/// <summary>
91113
/// Decodes the entropy coded data.
92114
/// </summary>
93-
public void ParseEntropyCodedData()
115+
public void ParseEntropyCodedData(int componentCount)
94116
{
95117
this.cancellationToken.ThrowIfCancellationRequested();
96118

119+
this.componentsCount = componentCount;
120+
97121
this.scanBuffer = new HuffmanScanBuffer(this.stream);
98122

99123
bool fullScan = this.frame.Progressive || this.frame.MultiScan;
@@ -124,7 +148,7 @@ public void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
124148

125149
private void ParseBaselineData()
126150
{
127-
if (this.ComponentsLength == this.frame.ComponentCount)
151+
if (this.componentsCount == this.frame.ComponentCount)
128152
{
129153
this.ParseBaselineDataInterleaved();
130154
}
@@ -143,13 +167,13 @@ private void ParseBaselineDataInterleaved()
143167
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
144168

145169
// Pre-derive the huffman table to avoid in-loop checks.
146-
for (int i = 0; i < this.ComponentsLength; i++)
170+
for (int i = 0; i < this.componentsCount; i++)
147171
{
148172
int order = this.frame.ComponentOrder[i];
149173
JpegComponent component = this.components[order];
150174

151-
ref HuffmanTable dcHuffmanTable = ref this.DcHuffmanTables[component.DCHuffmanTableId];
152-
ref HuffmanTable acHuffmanTable = ref this.AcHuffmanTables[component.ACHuffmanTableId];
175+
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
176+
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
153177
dcHuffmanTable.Configure();
154178
acHuffmanTable.Configure();
155179
}
@@ -163,13 +187,13 @@ private void ParseBaselineDataInterleaved()
163187
{
164188
// Scan an interleaved mcu... process components in order
165189
int mcuCol = mcu % mcusPerLine;
166-
for (int k = 0; k < this.ComponentsLength; k++)
190+
for (int k = 0; k < this.componentsCount; k++)
167191
{
168192
int order = this.frame.ComponentOrder[k];
169193
JpegComponent component = this.components[order];
170194

171-
ref HuffmanTable dcHuffmanTable = ref this.DcHuffmanTables[component.DCHuffmanTableId];
172-
ref HuffmanTable acHuffmanTable = ref this.AcHuffmanTables[component.ACHuffmanTableId];
195+
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
196+
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
173197

174198
int h = component.HorizontalSamplingFactor;
175199
int v = component.VerticalSamplingFactor;
@@ -221,8 +245,8 @@ private void ParseBaselineDataNonInterleaved()
221245
int w = component.WidthInBlocks;
222246
int h = component.HeightInBlocks;
223247

224-
ref HuffmanTable dcHuffmanTable = ref this.DcHuffmanTables[component.DCHuffmanTableId];
225-
ref HuffmanTable acHuffmanTable = ref this.AcHuffmanTables[component.ACHuffmanTableId];
248+
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
249+
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
226250
dcHuffmanTable.Configure();
227251
acHuffmanTable.Configure();
228252

@@ -272,7 +296,7 @@ private void CheckProgressiveData()
272296
}
273297

274298
// AC scans may have only one component.
275-
if (this.ComponentsLength != 1)
299+
if (this.componentsCount != 1)
276300
{
277301
invalid = true;
278302
}
@@ -304,7 +328,7 @@ private void ParseProgressiveData()
304328
{
305329
this.CheckProgressiveData();
306330

307-
if (this.ComponentsLength == 1)
331+
if (this.componentsCount == 1)
308332
{
309333
this.ParseProgressiveDataNonInterleaved();
310334
}
@@ -323,11 +347,11 @@ private void ParseProgressiveDataInterleaved()
323347
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
324348

325349
// Pre-derive the huffman table to avoid in-loop checks.
326-
for (int k = 0; k < this.ComponentsLength; k++)
350+
for (int k = 0; k < this.componentsCount; k++)
327351
{
328352
int order = this.frame.ComponentOrder[k];
329353
JpegComponent component = this.components[order];
330-
ref HuffmanTable dcHuffmanTable = ref this.DcHuffmanTables[component.DCHuffmanTableId];
354+
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
331355
dcHuffmanTable.Configure();
332356
}
333357

@@ -338,11 +362,11 @@ private void ParseProgressiveDataInterleaved()
338362
// Scan an interleaved mcu... process components in order
339363
int mcuRow = mcu / mcusPerLine;
340364
int mcuCol = mcu % mcusPerLine;
341-
for (int k = 0; k < this.ComponentsLength; k++)
365+
for (int k = 0; k < this.componentsCount; k++)
342366
{
343367
int order = this.frame.ComponentOrder[k];
344368
JpegComponent component = this.components[order];
345-
ref HuffmanTable dcHuffmanTable = ref this.DcHuffmanTables[component.DCHuffmanTableId];
369+
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
346370

347371
int h = component.HorizontalSamplingFactor;
348372
int v = component.VerticalSamplingFactor;
@@ -390,7 +414,7 @@ private void ParseProgressiveDataNonInterleaved()
390414

391415
if (this.SpectralStart == 0)
392416
{
393-
ref HuffmanTable dcHuffmanTable = ref this.DcHuffmanTables[component.DCHuffmanTableId];
417+
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
394418
dcHuffmanTable.Configure();
395419

396420
for (int j = 0; j < h; j++)
@@ -418,7 +442,7 @@ ref Unsafe.Add(ref blockRef, i),
418442
}
419443
else
420444
{
421-
ref HuffmanTable acHuffmanTable = ref this.AcHuffmanTables[component.ACHuffmanTableId];
445+
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
422446
acHuffmanTable.Configure();
423447

424448
for (int j = 0; j < h; j++)
@@ -722,5 +746,19 @@ private bool HandleRestart()
722746

723747
return false;
724748
}
749+
750+
/// <summary>
751+
/// Build the huffman table using code lengths and code values.
752+
/// </summary>
753+
/// <param name="type">Table type.</param>
754+
/// <param name="index">Table index.</param>
755+
/// <param name="codeLengths">Code lengths.</param>
756+
/// <param name="values">Code values.</param>
757+
[MethodImpl(InliningOptions.ShortMethod)]
758+
public void BuildHuffmanTable(int type, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
759+
{
760+
HuffmanTable[] tables = type == 0 ? this.dcHuffmanTables : this.acHuffmanTables;
761+
tables[index] = new HuffmanTable(codeLengths, values);
762+
}
725763
}
726764
}

src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
1111
/// </summary>
1212
internal interface IRawJpegData : IDisposable
1313
{
14-
/// <summary>
15-
/// Gets the image size in pixels.
16-
/// </summary>
17-
Size ImageSizeInPixels { get; }
18-
19-
/// <summary>
20-
/// Gets the number of components.
21-
/// </summary>
22-
int ComponentCount { get; }
23-
2414
/// <summary>
2515
/// Gets the color space
2616
/// </summary>
2717
JpegColorSpace ColorSpace { get; }
2818

29-
/// <summary>
30-
/// Gets the number of bits used for precision.
31-
/// </summary>
32-
int Precision { get; }
33-
3419
/// <summary>
3520
/// Gets the components.
3621
/// </summary>
@@ -41,4 +26,4 @@ internal interface IRawJpegData : IDisposable
4126
/// </summary>
4227
Block8x8F[] QuantizationTables { get; }
4328
}
44-
}
29+
}

src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ internal struct JpegBlockPostProcessor
3838
/// </summary>
3939
private Size subSamplingDivisors;
4040

41-
/// <summary>
42-
/// Defines the maximum value derived from the bitdepth.
43-
/// </summary>
44-
private readonly int maximumValue;
45-
4641
/// <summary>
4742
/// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct.
4843
/// </summary>
@@ -53,7 +48,6 @@ public JpegBlockPostProcessor(IRawJpegData decoder, IJpegComponent component)
5348
int qtIndex = component.QuantizationTableIndex;
5449
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
5550
this.subSamplingDivisors = component.SubSamplingDivisors;
56-
this.maximumValue = (int)MathF.Pow(2, decoder.Precision) - 1;
5751

5852
this.SourceBlock = default;
5953
this.WorkspaceBlock1 = default;

src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,24 @@ public void Dispose()
106106
this.SpectralBlocks = null;
107107
}
108108

109-
public void Init()
109+
/// <summary>
110+
/// Initializes component for future buffers initialization.
111+
/// </summary>
112+
/// <param name="maxSubFactorH">Maximal horizontal subsampling factor among all the components.</param>
113+
/// <param name="maxSubFactorV">Maximal vertical subsampling factor among all the components.</param>
114+
public void Init(int maxSubFactorH, int maxSubFactorV)
110115
{
111116
this.WidthInBlocks = (int)MathF.Ceiling(
112-
MathF.Ceiling(this.Frame.PixelWidth / 8F) * this.HorizontalSamplingFactor / this.Frame.MaxHorizontalFactor);
117+
MathF.Ceiling(this.Frame.PixelWidth / 8F) * this.HorizontalSamplingFactor / maxSubFactorH);
113118

114119
this.HeightInBlocks = (int)MathF.Ceiling(
115-
MathF.Ceiling(this.Frame.PixelHeight / 8F) * this.VerticalSamplingFactor / this.Frame.MaxVerticalFactor);
120+
MathF.Ceiling(this.Frame.PixelHeight / 8F) * this.VerticalSamplingFactor / maxSubFactorV);
116121

117122
int blocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor;
118123
int blocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor;
119124
this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu);
120125

121-
JpegComponent c0 = this.Frame.Components[0];
122-
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
126+
this.SubSamplingDivisors = new Size(maxSubFactorH, maxSubFactorV).DivideBy(this.SamplingFactors);
123127

124128
if (this.SubSamplingDivisors.Width == 0 || this.SubSamplingDivisors.Height == 0)
125129
{

src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,18 @@ internal class JpegComponentPostProcessor : IDisposable
2121
/// </summary>
2222
private readonly Size blockAreaSize;
2323

24+
/// <summary>
25+
/// Jpeg frame instance containing required decoding metadata.
26+
/// </summary>
27+
private readonly JpegFrame frame;
28+
2429
/// <summary>
2530
/// Initializes a new instance of the <see cref="JpegComponentPostProcessor"/> class.
2631
/// </summary>
27-
public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
32+
public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
2833
{
34+
this.frame = frame;
35+
2936
this.Component = component;
3037
this.RawJpeg = rawJpeg;
3138
this.blockAreaSize = this.Component.SubSamplingDivisors * 8;
@@ -70,7 +77,8 @@ public void CopyBlocksToColorBuffer(int step)
7077
Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks;
7178

7279
var blockPp = new JpegBlockPostProcessor(this.RawJpeg, this.Component);
73-
float maximumValue = MathF.Pow(2, this.RawJpeg.Precision) - 1;
80+
81+
float maximumValue = this.frame.MaxColorChannelValue;
7482

7583
int destAreaStride = this.ColorBuffer.Width;
7684

0 commit comments

Comments
 (0)