@ -44,6 +44,8 @@ namespace ImageSharp.Formats
/// </summary>
/// </summary>
private const int MaxTh = 3 ;
private const int MaxTh = 3 ;
private const int ThRowSize = MaxTh + 1 ;
/// <summary>
/// <summary>
/// The maximum number of quantization tables
/// The maximum number of quantization tables
/// </summary>
/// </summary>
@ -85,7 +87,9 @@ namespace ImageSharp.Formats
/// <summary>
/// <summary>
/// The huffman trees
/// The huffman trees
/// </summary>
/// </summary>
private readonly Huffman [ , ] huffmanTrees ;
//private readonly Huffman[,] huffmanTrees;
private readonly Huffman [ ] huffmanTrees ;
/// <summary>
/// <summary>
/// Quantization tables, in zigzag order.
/// Quantization tables, in zigzag order.
@ -187,12 +191,16 @@ namespace ImageSharp.Formats
/// </summary>
/// </summary>
private short verticalResolution ;
private short verticalResolution ;
private int blockIndex ;
/// <summary>
/// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore"/> class.
/// Initializes a new instance of the <see cref="JpegDecoderCore"/> class.
/// </summary>
/// </summary>
public JpegDecoderCore ( )
public JpegDecoderCore ( )
{
{
this . huffmanTrees = new Huffman [ MaxTc + 1 , MaxTh + 1 ] ;
//this.huffmanTrees = new Huffman[MaxTc + 1, MaxTh + 1];
this . huffmanTrees = new Huffman [ ( MaxTc + 1 ) * ( MaxTh + 1 ) ] ;
this . quantizationTables = new Block [ MaxTq + 1 ] ;
this . quantizationTables = new Block [ MaxTq + 1 ] ;
this . temp = new byte [ 2 * Block . BlockSize ] ;
this . temp = new byte [ 2 * Block . BlockSize ] ;
this . componentArray = new Component [ MaxComponents ] ;
this . componentArray = new Component [ MaxComponents ] ;
@ -201,23 +209,26 @@ namespace ImageSharp.Formats
this . bytes = new Bytes ( ) ;
this . bytes = new Bytes ( ) ;
// TODO: This looks like it could be static.
// TODO: This looks like it could be static.
for ( int i = 0 ; i < MaxTc + 1 ; i + + )
for ( int i = 0 ; i < MaxTc + 1 ; i + + )
{
{
for ( int j = 0 ; j < MaxTh + 1 ; j + + )
for ( int j = 0 ; j < MaxTh + 1 ; j + + )
{
{
this . huffmanTrees [ i , j ] = new Huffman ( LutSize , MaxNCodes , MaxCodeLength ) ;
//this.huffmanTrees[i, j].Init(LutSize, MaxNCodes, MaxCodeLength);
this . huffmanTrees [ i * ThRowSize + j ] . Init ( LutSize , MaxNCodes , MaxCodeLength ) ;
}
}
}
}
for ( int i = 0 ; i < this . quantizationTables . Length ; i + + )
for ( int i = 0 ; i < this . quantizationTables . Length ; i + + )
{
{
this . quantizationTables [ i ] = new Block ( ) ;
//this.quantizationTables[i] = new Block();
this . quantizationTables [ i ] . Init ( ) ;
}
}
for ( int i = 0 ; i < this . componentArray . Length ; i + + )
//for (int i = 0; i < this.componentArray.Length; i++)
{
//{
this . componentArray [ i ] = new Component ( ) ;
// this.componentArray[i] = new Component();
}
//}
}
}
@ -502,7 +513,12 @@ namespace ImageSharp.Formats
throw new ImageFormatException ( "Bad Th value" ) ;
throw new ImageFormatException ( "Bad Th value" ) ;
}
}
Huffman huffman = this . huffmanTrees [ tc , th ] ;
ProcessDefineHuffmanTablesMarkerLoop ( ref this . huffmanTrees [ tc * ThRowSize + th ] , ref remaining ) ;
}
}
private void ProcessDefineHuffmanTablesMarkerLoop ( ref Huffman huffman , ref int remaining )
{
// Read nCodes and huffman.Valuess (and derive h.Length).
// Read nCodes and huffman.Valuess (and derive h.Length).
// nCodes[i] is the number of codes with code length i.
// nCodes[i] is the number of codes with code length i.
@ -589,14 +605,13 @@ namespace ImageSharp.Formats
c < < = 1 ;
c < < = 1 ;
}
}
}
}
}
/// <summary>
/// <summary>
/// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value.
/// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value.
/// </summary>
/// </summary>
/// <param name="huffman">The huffman value</param>
/// <param name="huffman">The huffman value</param>
/// <returns>The <see cref="byte"/></returns>
/// <returns>The <see cref="byte"/></returns>
private byte DecodeHuffman ( Huffman huffman )
private byte DecodeHuffman ( ref Huffman huffman )
{
{
if ( huffman . Length = = 0 )
if ( huffman . Length = = 0 )
{
{
@ -1480,7 +1495,8 @@ namespace ImageSharp.Formats
this . ReadFull ( this . temp , 0 , remaining ) ;
this . ReadFull ( this . temp , 0 , remaining ) ;
byte scanComponentCount = this . temp [ 0 ] ;
byte scanComponentCount = this . temp [ 0 ] ;
if ( remaining ! = 4 + ( 2 * scanComponentCount ) )
int scanComponentCountBy2 = 2 * scanComponentCount ;
if ( remaining ! = 4 + scanComponentCountBy2 )
{
{
throw new ImageFormatException ( "SOS length inconsistent with number of components" ) ;
throw new ImageFormatException ( "SOS length inconsistent with number of components" ) ;
}
}
@ -1490,51 +1506,7 @@ namespace ImageSharp.Formats
for ( int i = 0 ; i < scanComponentCount ; i + + )
for ( int i = 0 ; i < scanComponentCount ; i + + )
{
{
// Component selector.
ProcessScanImpl ( i , ref scan [ i ] , scan , ref totalHv ) ;
int cs = this . temp [ 1 + ( 2 * i ) ] ;
int compIndex = - 1 ;
for ( int j = 0 ; j < this . componentCount ; j + + )
{
Component compv = this . componentArray [ j ] ;
if ( cs = = compv . Identifier )
{
compIndex = j ;
}
}
if ( compIndex < 0 )
{
throw new ImageFormatException ( "Unknown component selector" ) ;
}
scan [ i ] . Index = ( byte ) compIndex ;
// Section B.2.3 states that "the value of Cs_j shall be different from
// the values of Cs_1 through Cs_(j-1)". Since we have previously
// verified that a frame's component identifiers (C_i values in section
// B.2.2) are unique, it suffices to check that the implicit indexes
// into comp are unique.
for ( int j = 0 ; j < i ; j + + )
{
if ( scan [ i ] . Index = = scan [ j ] . Index )
{
throw new ImageFormatException ( "Repeated component selector" ) ;
}
}
totalHv + = this . componentArray [ compIndex ] . HorizontalFactor * this . componentArray [ compIndex ] . VerticalFactor ;
scan [ i ] . DcTableSelector = ( byte ) ( this . temp [ 2 + ( 2 * i ) ] > > 4 ) ;
if ( scan [ i ] . DcTableSelector > MaxTh )
{
throw new ImageFormatException ( "Bad DC table selector value" ) ;
}
scan [ i ] . AcTableSelector = ( byte ) ( this . temp [ 2 + ( 2 * i ) ] & 0x0f ) ;
if ( scan [ i ] . AcTableSelector > MaxTh )
{
throw new ImageFormatException ( "Bad AC table selector value" ) ;
}
}
}
// Section B.2.3 states that if there is more than one component then the
// Section B.2.3 states that if there is more than one component then the
@ -1565,10 +1537,10 @@ namespace ImageSharp.Formats
if ( this . isProgressive )
if ( this . isProgressive )
{
{
zigStart = this . temp [ 1 + ( 2 * scanComponentCount ) ] ;
zigStart = this . temp [ 1 + scanComponentCountBy2 ] ;
zigEnd = this . temp [ 2 + ( 2 * scanComponentCount ) ] ;
zigEnd = this . temp [ 2 + scanComponentCountBy2 ] ;
ah = this . temp [ 3 + ( 2 * scanComponentCount ) ] > > 4 ;
ah = this . temp [ 3 + scanComponentCountBy2 ] > > 4 ;
al = this . temp [ 3 + ( 2 * scanComponentCount ) ] & 0x0f ;
al = this . temp [ 3 + scanComponentCountBy2 ] & 0x0f ;
if ( ( zigStart = = 0 & & zigEnd ! = 0 ) | | zigStart > zigEnd | | Block . BlockSize < = zigEnd )
if ( ( zigStart = = 0 & & zigEnd ! = 0 ) | | zigStart > zigEnd | | Block . BlockSize < = zigEnd )
{
{
@ -1608,7 +1580,7 @@ namespace ImageSharp.Formats
for ( int j = 0 ; j < this . progCoeffs [ compIndex ] . Length ; j + + )
for ( int j = 0 ; j < this . progCoeffs [ compIndex ] . Length ; j + + )
{
{
this . progCoeffs [ compIndex ] [ j ] = new Block ( ) ;
this . progCoeffs [ compIndex ] [ j ] . Init ( ) ;
}
}
}
}
}
}
@ -1620,7 +1592,7 @@ namespace ImageSharp.Formats
byte expectedRst = JpegConstants . Markers . RST0 ;
byte expectedRst = JpegConstants . Markers . RST0 ;
// b is the decoded coefficients block, in natural (not zig-zag) order.
// b is the decoded coefficients block, in natural (not zig-zag) order.
Block b ;
//Block b;
int [ ] dc = new int [ MaxComponents ] ;
int [ ] dc = new int [ MaxComponents ] ;
// bx and by are the location of the current block, in units of 8x8
// bx and by are the location of the current block, in units of 8x8
@ -1636,7 +1608,7 @@ namespace ImageSharp.Formats
int compIndex = scan [ i ] . Index ;
int compIndex = scan [ i ] . Index ;
int hi = this . componentArray [ compIndex ] . HorizontalFactor ;
int hi = this . componentArray [ compIndex ] . HorizontalFactor ;
int vi = this . componentArray [ compIndex ] . VerticalFactor ;
int vi = this . componentArray [ compIndex ] . VerticalFactor ;
Block qt = this . quantizationTables [ this . componentArray [ compIndex ] . Selector ] ;
for ( int j = 0 ; j < hi * vi ; j + + )
for ( int j = 0 ; j < hi * vi ; j + + )
{
{
@ -1679,12 +1651,76 @@ namespace ImageSharp.Formats
}
}
}
}
var qtIndex = this . componentArray [ compIndex ] . Selector ;
// Load the previous partially decoded coefficients, if applicable.
// Load the previous partially decoded coefficients, if applicable.
b = this . isProgressive ? this . progCoeffs [ compIndex ] [ ( ( @by * mxx ) * hi ) + bx ] : new Block ( ) ;
//b = this.isProgressive ? this.progCoeffs[compIndex][blockIndex] : new Block();
if ( this . isProgressive )
{
blockIndex = ( ( @by * mxx ) * hi ) + bx ;
ProcessBlockImpl ( ah ,
ref this . progCoeffs [ compIndex ] [ blockIndex ] ,
scan , i , zigStart , zigEnd , al , dc , compIndex , @by , mxx , hi , bx ,
ref this . quantizationTables [ qtIndex ]
) ;
}
else
{
var b = new Block ( ) ;
b . Init ( ) ;
ProcessBlockImpl ( ah , ref b , scan , i , zigStart , zigEnd , al , dc , compIndex , @by , mxx , hi ,
bx , ref this . quantizationTables [ qtIndex ]
) ;
}
}
// for j
}
// for i
mcu + + ;
if ( this . restartInterval > 0 & & mcu % this . restartInterval = = 0 & & mcu < mxx * myy )
{
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
// but this one assumes well-formed input, and hence the restart marker follows immediately.
this . ReadFull ( this . temp , 0 , 2 ) ;
if ( this . temp [ 0 ] ! = 0xff | | this . temp [ 1 ] ! = expectedRst )
{
throw new ImageFormatException ( "Bad RST marker" ) ;
}
expectedRst + + ;
if ( expectedRst = = JpegConstants . Markers . RST7 + 1 )
{
expectedRst = JpegConstants . Markers . RST0 ;
}
// Reset the Huffman decoder.
this . bits = new Bits ( ) ;
// Reset the DC components, as per section F.2.1.3.1.
dc = new int [ MaxComponents ] ;
// Reset the progressive decoder state, as per section G.1.2.2.
this . eobRun = 0 ;
}
}
// for mx
}
// for my
}
private void ProcessBlockImpl ( int ah , ref Block b , Scan [ ] scan , int i , int zigStart , int zigEnd , int al ,
int [ ] dc , int compIndex , int @by , int mxx , int hi , int bx , ref Block qt )
{
if ( ah ! = 0 )
if ( ah ! = 0 )
{
{
this . Refine ( b , this . huffmanTrees [ AcTable , scan [ i ] . AcTableSelector ] , zigStart , zigEnd , 1 < < al ) ;
this . Refine ( ref b , ref this . huffmanTrees [ AcTable * ThRowSize + scan [ i ] . AcTableSelector ] , zigStart , zigEnd , 1 < < al ) ;
}
}
else
else
{
{
@ -1694,7 +1730,7 @@ namespace ImageSharp.Formats
zig + + ;
zig + + ;
// Decode the DC coefficient, as specified in section F.2.2.1.
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value = this . DecodeHuffman ( this . huffmanTrees [ DcTable , scan [ i ] . DcTableSelector ] ) ;
byte value = this . DecodeHuffman ( ref this . huffmanTrees [ DcTable * ThRowSize + scan [ i ] . DcTableSelector ] ) ;
if ( value > 1 6 )
if ( value > 1 6 )
{
{
throw new ImageFormatException ( "Excessive DC component" ) ;
throw new ImageFormatException ( "Excessive DC component" ) ;
@ -1712,10 +1748,10 @@ namespace ImageSharp.Formats
else
else
{
{
// Decode the AC coefficients, as specified in section F.2.2.2.
// Decode the AC coefficients, as specified in section F.2.2.2.
Huffman huffv = this . huffmanTrees [ AcTable , scan [ i ] . AcTableSelector ] ;
//Huffman huffv = ;
for ( ; zig < = zigEnd ; zig + + )
for ( ; zig < = zigEnd ; zig + + )
{
{
byte value = this . DecodeHuffman ( huffv ) ;
byte value = this . DecodeHuffman ( ref this . huffmanTrees [ AcTable * ThRowSize + scan [ i ] . AcTableSelector ] ) ;
byte val0 = ( byte ) ( value > > 4 ) ;
byte val0 = ( byte ) ( value > > 4 ) ;
byte val1 = ( byte ) ( value & 0x0f ) ;
byte val1 = ( byte ) ( value & 0x0f ) ;
if ( val1 ! = 0 )
if ( val1 ! = 0 )
@ -1754,14 +1790,14 @@ namespace ImageSharp.Formats
if ( zigEnd ! = Block . BlockSize - 1 | | al ! = 0 )
if ( zigEnd ! = Block . BlockSize - 1 | | al ! = 0 )
{
{
// We haven't completely decoded this 8x8 block. Save the coefficients.
// We haven't completely decoded this 8x8 block. Save the coefficients.
this . progCoeffs [ compIndex ] [ ( ( by * mxx ) * hi ) + bx ] = b ;
this . progCoeffs [ compIndex ] [ ( ( @by * mxx ) * hi ) + bx ] = b ;
// At this point, we could execute the rest of the loop body to dequantize and
// 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
// perform the inverse DCT, to save early stages of a progressive image to the
// *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
// *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
// the jpeg.Decode function does not return until the entire image is decoded,
// the jpeg.Decode function does not return until the entire image is decoded,
// so we "continue" here to avoid wasted computation.
// so we "continue" here to avoid wasted computation.
continue ;
return ;
}
}
}
}
@ -1771,7 +1807,7 @@ namespace ImageSharp.Formats
b [ Unzig [ zig ] ] * = qt [ zig ] ;
b [ Unzig [ zig ] ] * = qt [ zig ] ;
}
}
IDCT . Transform ( b ) ;
IDCT . Transform ( ref b ) ;
byte [ ] dst ;
byte [ ] dst ;
int offset ;
int offset ;
@ -1781,7 +1817,7 @@ namespace ImageSharp.Formats
{
{
dst = this . grayImage . Pixels ;
dst = this . grayImage . Pixels ;
stride = this . grayImage . Stride ;
stride = this . grayImage . Stride ;
offset = this . grayImage . Offset + ( 8 * ( ( by * this . grayImage . Stride ) + bx ) ) ;
offset = this . grayImage . Offset + ( 8 * ( ( @by * this . grayImage . Stride ) + bx ) ) ;
}
}
else
else
{
{
@ -1790,26 +1826,26 @@ namespace ImageSharp.Formats
case 0 :
case 0 :
dst = this . ycbcrImage . YChannel ;
dst = this . ycbcrImage . YChannel ;
stride = this . ycbcrImage . YStride ;
stride = this . ycbcrImage . YStride ;
offset = this . ycbcrImage . YOffset + ( 8 * ( ( by * this . ycbcrImage . YStride ) + bx ) ) ;
offset = this . ycbcrImage . YOffset + ( 8 * ( ( @by * this . ycbcrImage . YStride ) + bx ) ) ;
break ;
break ;
case 1 :
case 1 :
dst = this . ycbcrImage . CbChannel ;
dst = this . ycbcrImage . CbChannel ;
stride = this . ycbcrImage . CStride ;
stride = this . ycbcrImage . CStride ;
offset = this . ycbcrImage . COffset + ( 8 * ( ( by * this . ycbcrImage . CStride ) + bx ) ) ;
offset = this . ycbcrImage . COffset + ( 8 * ( ( @by * this . ycbcrImage . CStride ) + bx ) ) ;
break ;
break ;
case 2 :
case 2 :
dst = this . ycbcrImage . CrChannel ;
dst = this . ycbcrImage . CrChannel ;
stride = this . ycbcrImage . CStride ;
stride = this . ycbcrImage . CStride ;
offset = this . ycbcrImage . COffset + ( 8 * ( ( by * this . ycbcrImage . CStride ) + bx ) ) ;
offset = this . ycbcrImage . COffset + ( 8 * ( ( @by * this . ycbcrImage . CStride ) + bx ) ) ;
break ;
break ;
case 3 :
case 3 :
dst = this . blackPixels ;
dst = this . blackPixels ;
stride = this . blackStride ;
stride = this . blackStride ;
offset = 8 * ( ( by * this . blackStride ) + bx ) ;
offset = 8 * ( ( @by * this . blackStride ) + bx ) ;
break ;
break ;
default :
default :
@ -1844,43 +1880,59 @@ namespace ImageSharp.Formats
}
}
}
}
// for j
private void ProcessScanImpl ( int i , ref Scan currentScan , Scan [ ] scan , ref int totalHv )
}
// for i
mcu + + ;
if ( this . restartInterval > 0 & & mcu % this . restartInterval = = 0 & & mcu < mxx * myy )
{
{
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
// Component selector.
// but this one assumes well-formed input, and hence the restart marker follows immediately.
int cs = this . temp [ 1 + ( 2 * i ) ] ;
this . ReadFull ( this . temp , 0 , 2 ) ;
int compIndex = - 1 ;
if ( this . temp [ 0 ] ! = 0xff | | this . temp [ 1 ] ! = expectedRst )
for ( int j = 0 ; j < this . componentCount ; j + + )
{
{
throw new ImageFormatException ( "Bad RST marker" ) ;
//Component compv = ;
if ( cs = = this . componentArray [ j ] . Identifier )
{
compIndex = j ;
}
}
}
expectedRst + + ;
if ( compIndex < 0 )
if ( expectedRst = = JpegConstants . Markers . RST7 + 1 )
{
{
expectedRst = JpegConstants . Markers . RST0 ;
throw new ImageFormatException ( "Unknown component selector" ) ;
}
}
// Reset the Huffman decoder.
currentScan . Index = ( byte ) compIndex ;
this . bits = new Bits ( ) ;
// Reset the DC components, as per section F.2.1.3.1.
ProcessComponentImpl ( i , ref currentScan , scan , ref totalHv , ref this . componentArray [ compIndex ] ) ;
dc = new int [ MaxComponents ] ;
}
// Reset the progressive decoder state, as per section G.1.2.2.
private void ProcessComponentImpl ( int i , ref Scan currentScan , Scan [ ] scan , ref int totalHv , ref Component currentComponent )
this . eobRun = 0 ;
{
// Section B.2.3 states that "the value of Cs_j shall be different from
// the values of Cs_1 through Cs_(j-1)". Since we have previously
// verified that a frame's component identifiers (C_i values in section
// B.2.2) are unique, it suffices to check that the implicit indexes
// into comp are unique.
for ( int j = 0 ; j < i ; j + + )
{
if ( currentScan . Index = = scan [ j ] . Index )
{
throw new ImageFormatException ( "Repeated component selector" ) ;
}
}
}
}
// for mx
totalHv + = currentComponent . HorizontalFactor * currentComponent . VerticalFactor ;
currentScan . DcTableSelector = ( byte ) ( this . temp [ 2 + ( 2 * i ) ] > > 4 ) ;
if ( currentScan . DcTableSelector > MaxTh )
{
throw new ImageFormatException ( "Bad DC table selector value" ) ;
}
}
// for my
currentScan . AcTableSelector = ( byte ) ( this . temp [ 2 + ( 2 * i ) ] & 0x0f ) ;
if ( currentScan . AcTableSelector > MaxTh )
{
throw new ImageFormatException ( "Bad AC table selector value" ) ;
}
}
}
/// <summary>
/// <summary>
@ -1891,7 +1943,7 @@ namespace ImageSharp.Formats
/// <param name="zigStart">The zig-zag start index</param>
/// <param name="zigStart">The zig-zag start index</param>
/// <param name="zigEnd">The zig-zag end index</param>
/// <param name="zigEnd">The zig-zag end index</param>
/// <param name="delta">The low transform offset</param>
/// <param name="delta">The low transform offset</param>
private void Refine ( Block b , Huffman h , int zigStart , int zigEnd , int delta )
private void Refine ( ref Block b , ref Huffman h , int zigStart , int zigEnd , int delta )
{
{
// Refining a DC component is trivial.
// Refining a DC component is trivial.
if ( zigStart = = 0 )
if ( zigStart = = 0 )
@ -1918,7 +1970,7 @@ namespace ImageSharp.Formats
{
{
bool done = false ;
bool done = false ;
int z = 0 ;
int z = 0 ;
byte val = this . DecodeHuffman ( h ) ;
byte val = this . DecodeHuffman ( ref h ) ;
int val0 = val > > 4 ;
int val0 = val > > 4 ;
int val1 = val & 0x0f ;
int val1 = val & 0x0f ;