@ -16,29 +16,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary>
/// </summary>
internal class HuffmanScanDecoder
internal class HuffmanScanDecoder
{
{
private readonly JpegFrame frame ;
private readonly HuffmanTable [ ] dcHuffmanTables ;
private readonly HuffmanTable [ ] acHuffmanTables ;
private readonly BufferedReadStream stream ;
private readonly BufferedReadStream stream ;
private readonly JpegComponent [ ] components ;
// The restart interval.
// Frame related
private readonly int restartInterval ;
private JpegFrame frame ;
private JpegComponent [ ] components ;
// The number of interleaved components.
private readonly int componentsLength ;
// The spectral selection start.
private readonly int spectralStart ;
// The spectral selection end.
private readonly int spectralEnd ;
// The successive approximation high bit end.
private readonly int successiveHigh ;
// The successive approximation low bit end .
// The restart interval.
private readonly int successiveLow ;
private int restartInterval ;
// How many mcu's are left to do.
// How many mcu's are left to do.
private int todo ;
private int todo ;
@ -51,52 +36,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private HuffmanScanBuffer scanBuffer ;
private HuffmanScanBuffer scanBuffer ;
private readonly SpectralConverter spectralConverter ;
private CancellationToken cancellationToken ;
private CancellationToken cancellationToken ;
/// <summary>
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class.
/// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class.
/// </summary>
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="stream">The input stream.</param>
/// <param name="frame">The image frame.</param>
/// <param name="converter">Spectral to pixel converter.</param>
/// <param name="dcHuffmanTables">The DC Huffman tables.</param>
/// <param name="acHuffmanTables">The AC Huffman tables.</param>
/// <param name="componentsLength">The length of the components. Different to the array length.</param>
/// <param name="restartInterval">The reset interval.</param>
/// <param name="spectralStart">The spectral selection start.</param>
/// <param name="spectralEnd">The spectral selection end.</param>
/// <param name="successiveHigh">The successive approximation bit high end.</param>
/// <param name="successiveLow">The successive approximation bit low end.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param>
public HuffmanScanDecoder (
public HuffmanScanDecoder (
BufferedReadStream stream ,
BufferedReadStream stream ,
JpegFrame frame ,
SpectralConverter converter ,
HuffmanTable [ ] dcHuffmanTables ,
HuffmanTable [ ] acHuffmanTables ,
int componentsLength ,
int restartInterval ,
int spectralStart ,
int spectralEnd ,
int successiveHigh ,
int successiveLow ,
CancellationToken cancellationToken )
CancellationToken cancellationToken )
{
{
this . dctZigZag = ZigZag . CreateUnzigTable ( ) ;
this . dctZigZag = ZigZag . CreateUnzigTable ( ) ;
this . stream = stream ;
this . stream = stream ;
this . scanBuffer = new HuffmanScanBuffer ( stream ) ;
this . spectralConverter = converter ;
this . frame = frame ;
this . dcHuffmanTables = dcHuffmanTables ;
this . acHuffmanTables = acHuffmanTables ;
this . components = frame . Components ;
this . componentsLength = componentsLength ;
this . restartInterval = restartInterval ;
this . todo = restartInterval ;
this . spectralStart = spectralStart ;
this . spectralEnd = spectralEnd ;
this . successiveHigh = successiveHigh ;
this . successiveLow = successiveLow ;
this . cancellationToken = cancellationToken ;
this . cancellationToken = cancellationToken ;
}
}
// huffman tables
public HuffmanTable [ ] DcHuffmanTables { get ; set ; }
public HuffmanTable [ ] AcHuffmanTables { get ; set ; }
// Reset interval
public int ResetInterval
{
set
{
this . restartInterval = value ;
this . todo = value ;
}
}
// The number of interleaved components.
public int ComponentsLength { get ; set ; }
// The spectral selection start.
public int SpectralStart { get ; set ; }
// The spectral selection end.
public int SpectralEnd { get ; set ; }
// The successive approximation high bit end.
public int SuccessiveHigh { get ; set ; }
// The successive approximation low bit end.
public int SuccessiveLow { get ; set ; }
/// <summary>
/// <summary>
/// Decodes the entropy coded data.
/// Decodes the entropy coded data.
/// </summary>
/// </summary>
@ -104,6 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
{
this . cancellationToken . ThrowIfCancellationRequested ( ) ;
this . cancellationToken . ThrowIfCancellationRequested ( ) ;
this . scanBuffer = new HuffmanScanBuffer ( this . stream ) ;
bool fullScan = this . frame . Progressive | | this . frame . MultiScan ;
this . frame . AllocateComponents ( fullScan ) ;
if ( ! this . frame . Progressive )
if ( ! this . frame . Progressive )
{
{
this . ParseBaselineData ( ) ;
this . ParseBaselineData ( ) ;
@ -119,15 +114,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
}
}
public void InjectFrameData ( JpegFrame frame , IRawJpegData jpegData )
{
this . frame = frame ;
this . components = frame . Components ;
this . spectralConverter . InjectFrameData ( frame , jpegData ) ;
}
private void ParseBaselineData ( )
private void ParseBaselineData ( )
{
{
if ( this . componentsLength = = 1 )
if ( this . ComponentsLength = = this . frame . ComponentCount )
{
{
this . ParseBaselineDataNonInterleaved ( ) ;
this . ParseBaselineDataInterleaved ( ) ;
}
}
else
else
{
{
this . ParseBaselineDataInterleaved ( ) ;
this . ParseBaselineDataNon Interleaved ( ) ;
}
}
}
}
@ -140,13 +143,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
// Pre-derive the huffman table to avoid in-loop checks.
// Pre-derive the huffman table to avoid in-loop checks.
for ( int i = 0 ; i < this . c omponentsLength; i + + )
for ( int i = 0 ; i < this . C omponentsLength; i + + )
{
{
int order = this . frame . ComponentOrder [ i ] ;
int order = this . frame . ComponentOrder [ i ] ;
JpegComponent component = this . components [ order ] ;
JpegComponent component = this . components [ order ] ;
ref HuffmanTable dcHuffmanTable = ref this . d cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable dcHuffmanTable = ref this . D cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . a cHuffmanTables[ component . ACHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . A cHuffmanTables[ component . ACHuffmanTableId ] ;
dcHuffmanTable . Configure ( ) ;
dcHuffmanTable . Configure ( ) ;
acHuffmanTable . Configure ( ) ;
acHuffmanTable . Configure ( ) ;
}
}
@ -155,18 +158,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
{
this . cancellationToken . ThrowIfCancellationRequested ( ) ;
this . cancellationToken . ThrowIfCancellationRequested ( ) ;
// decode from binary to spectral
for ( int i = 0 ; i < mcusPerLine ; i + + )
for ( int i = 0 ; i < mcusPerLine ; i + + )
{
{
// Scan an interleaved mcu... process components in order
// Scan an interleaved mcu... process components in order
int mcuRow = mcu / mcusPerLine ;
int mcuCol = mcu % mcusPerLine ;
int mcuCol = mcu % mcusPerLine ;
for ( int k = 0 ; k < this . c omponentsLength; k + + )
for ( int k = 0 ; k < this . C omponentsLength; k + + )
{
{
int order = this . frame . ComponentOrder [ k ] ;
int order = this . frame . ComponentOrder [ k ] ;
JpegComponent component = this . components [ order ] ;
JpegComponent component = this . components [ order ] ;
ref HuffmanTable dcHuffmanTable = ref this . d cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable dcHuffmanTable = ref this . D cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . a cHuffmanTables[ component . ACHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . A cHuffmanTables[ component . ACHuffmanTableId ] ;
int h = component . HorizontalSamplingFactor ;
int h = component . HorizontalSamplingFactor ;
int v = component . VerticalSamplingFactor ;
int v = component . VerticalSamplingFactor ;
@ -175,14 +178,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
// by the basic H and V specified for the component
for ( int y = 0 ; y < v ; y + + )
for ( int y = 0 ; y < v ; y + + )
{
{
int blockRow = ( mcuRow * v ) + y ;
Span < Block8x8 > blockSpan = component . SpectralBlocks . GetRowSpan ( y ) ;
Span < Block8x8 > blockSpan = component . SpectralBlocks . GetRowSpan ( blockRow ) ;
ref Block8x8 blockRef = ref MemoryMarshal . GetReference ( blockSpan ) ;
ref Block8x8 blockRef = ref MemoryMarshal . GetReference ( blockSpan ) ;
for ( int x = 0 ; x < h ; x + + )
for ( int x = 0 ; x < h ; x + + )
{
{
if ( buffer . NoData )
if ( buffer . NoData )
{
{
// It is very likely that some spectral data was decoded before we encountered EOI marker
// so we need to decode what's left and return (or maybe throw?)
this . spectralConverter . ConvertStrideBaseline ( ) ;
return ;
return ;
}
}
@ -202,6 +207,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
mcu + + ;
mcu + + ;
this . HandleRestart ( ) ;
this . HandleRestart ( ) ;
}
}
// convert from spectral to actual pixels via given converter
this . spectralConverter . ConvertStrideBaseline ( ) ;
}
}
}
}
@ -213,8 +221,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int w = component . WidthInBlocks ;
int w = component . WidthInBlocks ;
int h = component . HeightInBlocks ;
int h = component . HeightInBlocks ;
ref HuffmanTable dcHuffmanTable = ref this . d cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable dcHuffmanTable = ref this . D cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . a cHuffmanTables[ component . ACHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . A cHuffmanTables[ component . ACHuffmanTableId ] ;
dcHuffmanTable . Configure ( ) ;
dcHuffmanTable . Configure ( ) ;
acHuffmanTable . Configure ( ) ;
acHuffmanTable . Configure ( ) ;
@ -248,9 +256,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Logic has been adapted from libjpeg.
// Logic has been adapted from libjpeg.
// See Table B.3 – Scan header parameter size and values. itu-t81.pdf
// See Table B.3 – Scan header parameter size and values. itu-t81.pdf
bool invalid = false ;
bool invalid = false ;
if ( this . s pectralStart = = 0 )
if ( this . S pectralStart = = 0 )
{
{
if ( this . s pectralEnd ! = 0 )
if ( this . S pectralEnd ! = 0 )
{
{
invalid = true ;
invalid = true ;
}
}
@ -258,22 +266,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
else
else
{
{
// Need not check Ss/Se < 0 since they came from unsigned bytes.
// Need not check Ss/Se < 0 since they came from unsigned bytes.
if ( this . s pectralEnd < this . s pectralStart | | this . s pectralEnd > 6 3 )
if ( this . S pectralEnd < this . S pectralStart | | this . S pectralEnd > 6 3 )
{
{
invalid = true ;
invalid = true ;
}
}
// AC scans may have only one component.
// AC scans may have only one component.
if ( this . c omponentsLength ! = 1 )
if ( this . C omponentsLength ! = 1 )
{
{
invalid = true ;
invalid = true ;
}
}
}
}
if ( this . s uccessiveHigh ! = 0 )
if ( this . S uccessiveHigh ! = 0 )
{
{
// Successive approximation refinement scan: must have Al = Ah-1.
// Successive approximation refinement scan: must have Al = Ah-1.
if ( this . s uccessiveHigh - 1 ! = this . s uccessiveLow)
if ( this . S uccessiveHigh - 1 ! = this . S uccessiveLow)
{
{
invalid = true ;
invalid = true ;
}
}
@ -281,14 +289,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// TODO: How does this affect 12bit jpegs.
// TODO: How does this affect 12bit jpegs.
// According to libjpeg the range covers 8bit only?
// According to libjpeg the range covers 8bit only?
if ( this . s uccessiveLow > 1 3 )
if ( this . S uccessiveLow > 1 3 )
{
{
invalid = true ;
invalid = true ;
}
}
if ( invalid )
if ( invalid )
{
{
JpegThrowHelper . ThrowBadProgressiveScan ( this . s pectralStart, this . s pectralEnd, this . s uccessiveHigh, this . s uccessiveLow) ;
JpegThrowHelper . ThrowBadProgressiveScan ( this . S pectralStart, this . S pectralEnd, this . S uccessiveHigh, this . S uccessiveLow) ;
}
}
}
}
@ -296,7 +304,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
{
this . CheckProgressiveData ( ) ;
this . CheckProgressiveData ( ) ;
if ( this . c omponentsLength = = 1 )
if ( this . C omponentsLength = = 1 )
{
{
this . ParseProgressiveDataNonInterleaved ( ) ;
this . ParseProgressiveDataNonInterleaved ( ) ;
}
}
@ -315,11 +323,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
// Pre-derive the huffman table to avoid in-loop checks.
// Pre-derive the huffman table to avoid in-loop checks.
for ( int k = 0 ; k < this . c omponentsLength; k + + )
for ( int k = 0 ; k < this . C omponentsLength; k + + )
{
{
int order = this . frame . ComponentOrder [ k ] ;
int order = this . frame . ComponentOrder [ k ] ;
JpegComponent component = this . components [ order ] ;
JpegComponent component = this . components [ order ] ;
ref HuffmanTable dcHuffmanTable = ref this . d cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable dcHuffmanTable = ref this . D cHuffmanTables[ component . DCHuffmanTableId ] ;
dcHuffmanTable . Configure ( ) ;
dcHuffmanTable . Configure ( ) ;
}
}
@ -330,11 +338,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Scan an interleaved mcu... process components in order
// Scan an interleaved mcu... process components in order
int mcuRow = mcu / mcusPerLine ;
int mcuRow = mcu / mcusPerLine ;
int mcuCol = mcu % mcusPerLine ;
int mcuCol = mcu % mcusPerLine ;
for ( int k = 0 ; k < this . c omponentsLength; k + + )
for ( int k = 0 ; k < this . C omponentsLength; k + + )
{
{
int order = this . frame . ComponentOrder [ k ] ;
int order = this . frame . ComponentOrder [ k ] ;
JpegComponent component = this . components [ order ] ;
JpegComponent component = this . components [ order ] ;
ref HuffmanTable dcHuffmanTable = ref this . d cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable dcHuffmanTable = ref this . D cHuffmanTables[ component . DCHuffmanTableId ] ;
int h = component . HorizontalSamplingFactor ;
int h = component . HorizontalSamplingFactor ;
int v = component . VerticalSamplingFactor ;
int v = component . VerticalSamplingFactor ;
@ -380,9 +388,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int w = component . WidthInBlocks ;
int w = component . WidthInBlocks ;
int h = component . HeightInBlocks ;
int h = component . HeightInBlocks ;
if ( this . s pectralStart = = 0 )
if ( this . S pectralStart = = 0 )
{
{
ref HuffmanTable dcHuffmanTable = ref this . d cHuffmanTables[ component . DCHuffmanTableId ] ;
ref HuffmanTable dcHuffmanTable = ref this . D cHuffmanTables[ component . DCHuffmanTableId ] ;
dcHuffmanTable . Configure ( ) ;
dcHuffmanTable . Configure ( ) ;
for ( int j = 0 ; j < h ; j + + )
for ( int j = 0 ; j < h ; j + + )
@ -410,7 +418,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
else
else
{
{
ref HuffmanTable acHuffmanTable = ref this . a cHuffmanTables[ component . ACHuffmanTableId ] ;
ref HuffmanTable acHuffmanTable = ref this . A cHuffmanTables[ component . ACHuffmanTableId ] ;
acHuffmanTable . Configure ( ) ;
acHuffmanTable . Configure ( ) ;
for ( int j = 0 ; j < h ; j + + )
for ( int j = 0 ; j < h ; j + + )
@ -489,7 +497,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref short blockDataRef = ref Unsafe . As < Block8x8 , short > ( ref block ) ;
ref short blockDataRef = ref Unsafe . As < Block8x8 , short > ( ref block ) ;
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
if ( this . s uccessiveHigh = = 0 )
if ( this . S uccessiveHigh = = 0 )
{
{
// First scan for DC coefficient, must be first
// First scan for DC coefficient, must be first
int s = buffer . DecodeHuffman ( ref dcTable ) ;
int s = buffer . DecodeHuffman ( ref dcTable ) ;
@ -500,20 +508,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
s + = component . DcPredictor ;
s + = component . DcPredictor ;
component . DcPredictor = s ;
component . DcPredictor = s ;
blockDataRef = ( short ) ( s < < this . s uccessiveLow) ;
blockDataRef = ( short ) ( s < < this . S uccessiveLow) ;
}
}
else
else
{
{
// Refinement scan for DC coefficient
// Refinement scan for DC coefficient
buffer . CheckBits ( ) ;
buffer . CheckBits ( ) ;
blockDataRef | = ( short ) ( buffer . GetBits ( 1 ) < < this . s uccessiveLow) ;
blockDataRef | = ( short ) ( buffer . GetBits ( 1 ) < < this . S uccessiveLow) ;
}
}
}
}
private void DecodeBlockProgressiveAC ( ref Block8x8 block , ref HuffmanTable acTable )
private void DecodeBlockProgressiveAC ( ref Block8x8 block , ref HuffmanTable acTable )
{
{
ref short blockDataRef = ref Unsafe . As < Block8x8 , short > ( ref block ) ;
ref short blockDataRef = ref Unsafe . As < Block8x8 , short > ( ref block ) ;
if ( this . s uccessiveHigh = = 0 )
if ( this . S uccessiveHigh = = 0 )
{
{
// MCU decoding for AC initial scan (either spectral selection,
// MCU decoding for AC initial scan (either spectral selection,
// or first pass of successive approximation).
// or first pass of successive approximation).
@ -525,9 +533,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref ZigZag zigzag = ref this . dctZigZag ;
ref ZigZag zigzag = ref this . dctZigZag ;
int start = this . s pectralStart;
int start = this . S pectralStart;
int end = this . s pectralEnd;
int end = this . S pectralEnd;
int low = this . s uccessiveLow;
int low = this . S uccessiveLow;
for ( int i = start ; i < = end ; + + i )
for ( int i = start ; i < = end ; + + i )
{
{
@ -571,11 +579,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Refinement scan for these AC coefficients
// Refinement scan for these AC coefficients
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref HuffmanScanBuffer buffer = ref this . scanBuffer ;
ref ZigZag zigzag = ref this . dctZigZag ;
ref ZigZag zigzag = ref this . dctZigZag ;
int start = this . s pectralStart;
int start = this . S pectralStart;
int end = this . s pectralEnd;
int end = this . S pectralEnd;
int p1 = 1 < < this . s uccessiveLow;
int p1 = 1 < < this . S uccessiveLow;
int m1 = ( - 1 ) < < this . s uccessiveLow;
int m1 = ( - 1 ) < < this . S uccessiveLow;
int k = start ;
int k = start ;