@ -1,4 +1,9 @@
// ReSharper disable InconsistentNaming
// <copyright file="JpegScanDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats.Jpg
{
using System ;
@ -12,128 +17,24 @@ namespace ImageSharp.Formats.Jpg
internal unsafe struct JpegScanDecoder
{
/// <summary>
/// The AC table index
/// Number of MCU-s (Minimum Coded Units) in the image along the X axis
/// </summary>
internal const int AcTableIndex = 1 ;
public int XNumberOfMCUs ;
/// <summary>
/// The DC table index
/// Number of MCU-s (Minimum Coded Units) in the image along the Y axis
/// </summary>
internal const int DcTableIndex = 0 ;
public int YNumberOfMCUs ;
/// <summary>
/// Holds the "large" data blocks needed for computations
/// The AC table index
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ComputationData
{
/// <summary>
/// The main input block
/// </summary>
public Block8x8F Block ;
/// <summary>
/// Temporal block 1 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp1 ;
/// <summary>
/// Temporal block 2 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp2 ;
/// <summary>
/// The quantization table as <see cref="Block8x8F"/>
/// </summary>
public Block8x8F QuantiazationTable ;
/// <summary>
/// The jpeg unzig data
/// </summary>
public UnzigData Unzig ;
/// <summary>
/// The no-idea-what's this data
/// </summary>
public fixed byte ScanData [ 3 * JpegDecoderCore . MaxComponents ] ;
/// <summary>
/// The DC component values
/// </summary>
public fixed int Dc [ JpegDecoderCore . MaxComponents ] ;
/// <summary>
/// Creates and initializes a new <see cref="ComputationData"/> instance
/// </summary>
/// <returns></returns>
public static ComputationData Create ( )
{
ComputationData data = default ( ComputationData ) ;
data . Unzig = UnzigData . Create ( ) ;
return data ;
}
}
private const int AcTableIndex = 1 ;
/// <summary>
/// Contains pointers to the memory regions of <see cref="ComputationData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
/// The DC table index
/// </summary>
public struct DataPointers
{
/// <summary>
/// Pointer to <see cref="ComputationData.Block"/>
/// </summary>
public Block8x8F * Block ;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp1"/>
/// </summary>
public Block8x8F * Temp1 ;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp2"/>
/// </summary>
public Block8x8F * Temp2 ;
/// <summary>
/// Pointer to <see cref="ComputationData.QuantiazationTable"/>
/// </summary>
public Block8x8F * QuantiazationTable ;
/// <summary>
/// Pointer to <see cref="ComputationData.Unzig"/> as int*
/// </summary>
public int * Unzig ;
/// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary>
public Scan * Scan ;
/// <summary>
/// Pointer to <see cref="ComputationData.Dc"/>
/// </summary>
public int * Dc ;
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 . Scan = ( Scan * ) basePtr - > ScanData ;
this . Dc = basePtr - > Dc ;
}
}
private void ResetDc ( )
{
Unsafe . InitBlock ( this . Pointers . Dc , default ( byte ) , sizeof ( int ) * JpegDecoderCore . MaxComponents ) ;
}
// bx and by are the
// blocks: the third block in the first row has (bx, by) = (2, 0).
private const int DcTableIndex = 0 ;
/// <summary>
/// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
@ -180,32 +81,25 @@ namespace ImageSharp.Formats.Jpg
/// </summary>
private int al ;
// XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
/// <summary>
/// Number of MCU-s (Minimum Coded Units) on X axis
/// </summary>
public int XNumberOfMCUs ;
/// <summary>
/// Number of MCU-s (Minimum Coded Units) in Y axis
/// The number of component scans
/// </summary>
public int YNumberOfMCUs ;
private int componentScanCount ;
/// <summary>
/// The number of component scans
/// End-of-Band run, specified in section G.1.2.2.
/// </summary>
private int componentScanCount ;
private ushort eobRun ;
/// <summary>
/// The <see cref="ComputationData"/> buffer
/// </summary>
private ComputationData D ata;
private ComputationData d ata;
/// <summary>
/// Pointers to elements of <see cref="D ata"/>
/// Pointers to elements of <see cref="d ata"/>
/// </summary>
private DataPointers P ointers;
private DataPointers p ointers;
/// <summary>
/// Initializes the default instance after creation.
@ -215,8 +109,8 @@ namespace ImageSharp.Formats.Jpg
/// <param name="remaining">The remaining bytes in the segment block.</param>
public static void Init ( JpegScanDecoder * p , JpegDecoderCore decoder , int remaining )
{
p - > D ata = ComputationData . Create ( ) ;
p - > P ointers = new DataPointers ( & p - > D ata) ;
p - > d ata = ComputationData . Create ( ) ;
p - > p ointers = new DataPointers ( & p - > d ata) ;
p - > InitImpl ( decoder , remaining ) ;
}
@ -236,7 +130,7 @@ namespace ImageSharp.Formats.Jpg
{
for ( int i = 0 ; i < this . componentScanCount ; i + + )
{
int compIndex = this . P ointers. Scan [ i ] . Index ;
int compIndex = this . p ointers. Scan [ i ] . Index ;
int hi = decoder . ComponentArray [ compIndex ] . HorizontalFactor ;
int vi = decoder . ComponentArray [ compIndex ] . VerticalFactor ;
@ -284,18 +178,17 @@ namespace ImageSharp.Formats.Jpg
int qtIndex = decoder . ComponentArray [ compIndex ] . Selector ;
// TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async.
this . data . QuantiazationTable = decoder . QuantizationTables [ qtIndex ] ;
this . Data . QuantiazationTable = decoder . QuantizationTables [ qtIndex ] ;
//Load the previous partially decoded coefficients, if applicable.
// Load the previous partially decoded coefficients, if applicable.
if ( decoder . IsProgressive )
{
int blockIndex = ( ( this . by * this . XNumberOfMCUs ) * hi ) + this . bx ;
this . D ata. Block = decoder . ProgCoeffs [ compIndex ] [ blockIndex ] ;
this . d ata. Block = decoder . ProgCoeffs [ compIndex ] [ blockIndex ] ;
}
else
{
this . D ata. Block . Clear ( ) ;
this . d ata. Block . Clear ( ) ;
}
this . ProcessBlockImpl ( decoder , i , compIndex , hi ) ;
@ -330,14 +223,19 @@ namespace ImageSharp.Formats.Jpg
this . ResetDc ( ) ;
// Reset the progressive decoder state, as per section G.1.2.2.
decoder . E obRun = 0 ;
this . e obRun = 0 ;
}
}
// for mx
}
}
private void ResetDc ( )
{
Unsafe . InitBlock ( this . pointers . Dc , default ( byte ) , sizeof ( int ) * JpegDecoderCore . MaxComponents ) ;
}
/// <summary>
/// The implementation part of <see cref="Init"/> as an instance method.
/// </summary>
@ -368,7 +266,7 @@ namespace ImageSharp.Formats.Jpg
for ( int i = 0 ; i < this . componentScanCount ; i + + )
{
this . ProcessScanImpl ( decoder , i , ref this . P ointers. Scan [ i ] , ref totalHv ) ;
this . ProcessScanImpl ( decoder , i , ref this . p ointers. Scan [ i ] , ref totalHv ) ;
}
// Section B.2.3 states that if there is more than one component then the
@ -414,7 +312,7 @@ namespace ImageSharp.Formats.Jpg
{
for ( int i = 0 ; i < this . componentScanCount ; i + + )
{
int compIndex = this . P ointers. Scan [ i ] . Index ;
int compIndex = this . p ointers. Scan [ i ] . Index ;
if ( decoder . ProgCoeffs [ compIndex ] = = null )
{
int size = this . XNumberOfMCUs * this . YNumberOfMCUs * decoder . ComponentArray [ compIndex ] . HorizontalFactor
@ -435,9 +333,9 @@ namespace ImageSharp.Formats.Jpg
/// <param name="hi">Horizontal sampling factor at the given component index</param>
private void ProcessBlockImpl ( JpegDecoderCore decoder , int i , int compIndex , int hi )
{
var b = this . P ointers. Block ;
//var dc = this.Pointers.Dc;
int huffmannIdx = ( AcTableIndex * HuffmanTree . ThRowSize ) + this . P ointers. Scan [ i ] . AcTableSelector ;
var b = this . p ointers. Block ;
int huffmannIdx = ( AcTableIndex * HuffmanTree . ThRowSize ) + this . p ointers. Scan [ i ] . AcTableSelector ;
if ( this . ah ! = 0 )
{
this . Refine ( decoder , ref decoder . HuffmanTrees [ huffmannIdx ] , 1 < < this . al ) ;
@ -452,22 +350,22 @@ namespace ImageSharp.Formats.Jpg
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value =
decoder . DecodeHuffman (
ref decoder . HuffmanTrees [ ( DcTableIndex * HuffmanTree . ThRowSize ) + this . P ointers. Scan [ i ] . DcTableSelector ] ) ;
ref decoder . HuffmanTrees [ ( DcTableIndex * HuffmanTree . ThRowSize ) + this . p ointers. Scan [ i ] . DcTableSelector ] ) ;
if ( value > 1 6 )
{
throw new ImageFormatException ( "Excessive DC component" ) ;
}
int deltaDC = decoder . Bits . ReceiveExtend ( value , decoder ) ;
this . P ointers. Dc [ compIndex ] + = deltaDC ;
this . p ointers. Dc [ compIndex ] + = deltaDC ;
// b[0] = dc[compIndex] << al;
Block8x8F . SetScalarAt ( b , 0 , this . P ointers. Dc [ compIndex ] < < al ) ;
Block8x8F . SetScalarAt ( b , 0 , this . p ointers. Dc [ compIndex ] < < this . al ) ;
}
if ( zig < = this . zigEnd & & decoder . E obRun > 0 )
if ( zig < = this . zigEnd & & this . e obRun > 0 )
{
decoder . E obRun- - ;
this . e obRun- - ;
}
else
{
@ -488,19 +386,19 @@ namespace ImageSharp.Formats.Jpg
int ac = decoder . Bits . ReceiveExtend ( val1 , decoder ) ;
// b[Unzig[zig]] = ac << al;
Block8x8F . SetScalarAt ( b , this . P ointers. Unzig [ zig ] , ac < < this . al ) ;
Block8x8F . SetScalarAt ( b , this . p ointers. Unzig [ zig ] , ac < < this . al ) ;
}
else
{
if ( val0 ! = 0x0f )
{
decoder . E obRun = ( ushort ) ( 1 < < val0 ) ;
this . e obRun = ( ushort ) ( 1 < < val0 ) ;
if ( val0 ! = 0 )
{
decoder . E obRun | = ( ushort ) decoder . DecodeBits ( val0 ) ;
this . e obRun | = ( ushort ) decoder . DecodeBits ( val0 ) ;
}
decoder . E obRun- - ;
this . e obRun- - ;
break ;
}
@ -514,7 +412,7 @@ namespace ImageSharp.Formats.Jpg
{
if ( this . zigEnd ! = Block8x8F . ScalarCount - 1 | | this . 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 * XNumberOfMCUs) * hi) + bx] = b.Clone();
decoder . ProgCoeffs [ compIndex ] [ ( ( this . by * this . XNumberOfMCUs ) * hi ) + this . bx ] = * b ;
@ -528,13 +426,13 @@ namespace ImageSharp.Formats.Jpg
}
// Dequantize, perform the inverse DCT and store the block to the image.
Block8x8F . UnZig ( b , this . P ointers. QuantiazationTable , this . P ointers. Unzig ) ;
Block8x8F . UnZig ( b , this . p ointers. QuantiazationTable , this . p ointers. Unzig ) ;
DCT . TransformIDCT ( ref * b , ref * this . P ointers. Temp1 , ref * this . P ointers. Temp2 ) ;
DCT . TransformIDCT ( ref * b , ref * this . p ointers. Temp1 , ref * this . p ointers. Temp2 ) ;
var destChannel = decoder . GetDestinationChannel ( compIndex ) ;
var destArea = destChannel . GetOffsetedSubAreaForBlock ( this . bx , this . by ) ;
destArea . LoadColorsFrom ( this . P ointers. Temp1 , this . P ointers. Temp2 ) ;
destArea . LoadColorsFrom ( this . p ointers. Temp1 , this . p ointers. Temp2 ) ;
}
private void ProcessScanImpl ( JpegDecoderCore decoder , int i , ref Scan currentScan , ref int totalHv )
@ -561,7 +459,6 @@ namespace ImageSharp.Formats.Jpg
this . ProcessComponentImpl ( decoder , i , ref currentScan , ref totalHv , ref decoder . ComponentArray [ compIndex ] ) ;
}
private void ProcessComponentImpl (
JpegDecoderCore decoder ,
int i ,
@ -576,7 +473,7 @@ namespace ImageSharp.Formats.Jpg
// into comp are unique.
for ( int j = 0 ; j < i ; j + + )
{
if ( currentScan . Index = = this . P ointers. Scan [ j ] . Index )
if ( currentScan . Index = = this . p ointers. Scan [ j ] . Index )
{
throw new ImageFormatException ( "Repeated component selector" ) ;
}
@ -600,12 +497,13 @@ namespace ImageSharp.Formats.Jpg
/// <summary>
/// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary>
/// <param name="decoder">The decoder instance</param>
/// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param>
/// <param name="decoder">The decoder instance</param>
private void Refine ( JpegDecoderCore decoder , ref HuffmanTree h , int delta )
{
Block8x8F * b = this . Pointers . Block ;
Block8x8F * b = this . pointers . Block ;
// Refining a DC component is trivial.
if ( this . zigStart = = 0 )
{
@ -631,7 +529,7 @@ namespace ImageSharp.Formats.Jpg
// Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
int zig = this . zigStart ;
if ( decoder . E obRun = = 0 )
if ( this . e obRun = = 0 )
{
for ( ; zig < = this . zigEnd ; zig + + )
{
@ -646,10 +544,10 @@ namespace ImageSharp.Formats.Jpg
case 0 :
if ( val0 ! = 0x0f )
{
decoder . E obRun = ( ushort ) ( 1 < < val0 ) ;
this . e obRun = ( ushort ) ( 1 < < val0 ) ;
if ( val0 ! = 0 )
{
decoder . E obRun | = ( ushort ) decoder . DecodeBits ( val0 ) ;
this . e obRun | = ( ushort ) decoder . DecodeBits ( val0 ) ;
}
done = true ;
@ -683,14 +581,14 @@ namespace ImageSharp.Formats.Jpg
if ( z ! = 0 )
{
// b[Unzig[zig]] = z;
Block8x8F . SetScalarAt ( b , this . P ointers. Unzig [ zig ] , z ) ;
Block8x8F . SetScalarAt ( b , this . p ointers. Unzig [ zig ] , z ) ;
}
}
}
if ( decoder . E obRun > 0 )
if ( this . e obRun > 0 )
{
decoder . E obRun- - ;
this . e obRun- - ;
this . RefineNonZeroes ( decoder , zig , - 1 , delta ) ;
}
}
@ -706,10 +604,10 @@ namespace ImageSharp.Formats.Jpg
/// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes ( JpegDecoderCore decoder , int zig , int nz , int delta )
{
var b = this . P ointers. Block ;
var b = this . p ointers. Block ;
for ( ; zig < = this . zigEnd ; zig + + )
{
int u = this . P ointers. Unzig [ zig ] ;
int u = this . p ointers. Unzig [ zig ] ;
float bu = Block8x8F . GetScalarAt ( b , u ) ;
// TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
@ -744,5 +642,114 @@ namespace ImageSharp.Formats.Jpg
return zig ;
}
/// <summary>
/// Holds the "large" data blocks needed for computations
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ComputationData
{
/// <summary>
/// The main input block
/// </summary>
public Block8x8F Block ;
/// <summary>
/// Temporal block 1 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp1 ;
/// <summary>
/// Temporal block 2 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp2 ;
/// <summary>
/// The quantization table as <see cref="Block8x8F"/>
/// </summary>
public Block8x8F QuantiazationTable ;
/// <summary>
/// The jpeg unzig data
/// </summary>
public UnzigData Unzig ;
/// <summary>
/// The no-idea-what's this data
/// </summary>
public fixed byte ScanData [ 3 * JpegDecoderCore . MaxComponents ] ;
/// <summary>
/// The DC component values
/// </summary>
public fixed int Dc [ JpegDecoderCore . MaxComponents ] ;
/// <summary>
/// Creates and initializes a new <see cref="ComputationData"/> instance
/// </summary>
/// <returns>The <see cref="ComputationData"/></returns>
public static ComputationData Create ( )
{
ComputationData data = default ( ComputationData ) ;
data . Unzig = UnzigData . Create ( ) ;
return data ;
}
}
/// <summary>
/// Contains pointers to the memory regions of <see cref="ComputationData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
/// </summary>
public struct DataPointers
{
/// <summary>
/// Pointer to <see cref="ComputationData.Block"/>
/// </summary>
public Block8x8F * Block ;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp1"/>
/// </summary>
public Block8x8F * Temp1 ;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp2"/>
/// </summary>
public Block8x8F * Temp2 ;
/// <summary>
/// Pointer to <see cref="ComputationData.QuantiazationTable"/>
/// </summary>
public Block8x8F * QuantiazationTable ;
/// <summary>
/// Pointer to <see cref="ComputationData.Unzig"/> as int*
/// </summary>
public int * Unzig ;
/// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary>
public Scan * Scan ;
/// <summary>
/// Pointer to <see cref="ComputationData.Dc"/>
/// </summary>
public int * Dc ;
/// <summary>
/// Initializes a new instance of the <see cref="DataPointers" /> struct.
/// </summary>
/// <param name="basePtr">The pointer pointing to <see cref="ComputationData"/></param>
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 . Scan = ( Scan * ) basePtr - > ScanData ;
this . Dc = basePtr - > Dc ;
}
}
}
}