/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - Initial contribution
*
* Contributors:
* Ixonos Plc
*
* Description:
*
*/
#include "CJpRotate.h"
#include "MJpRotateCallBack.h"
#include "CExifParser.h"
const TUint8 KZigZag[] = {
0,8,1,2,9,16,24,17,
10,3,4,11,18,25,32,40,
33,26,19,12,5,6,13,20,
27,34,41,48,56,49,42,35,
28,21,14,7,15,22,29,36,
43,50,57,58,51,44,37,30,
23,31,38,45,52,59,60,53,
46,39,47,54,61,62,55,63
};
const TUint8 KReZig[] = {
0,2,1,5,4,3,9,8,
7,6,14,13,12,11,10,20,
19,18,17,16,15,27,26,25,
24,23,22,21,35,34,33,32,
31,30,29,28,42,41,40,39,
38,37,36,48,47,46,45,44,
43,53,52,51,50,49,57,56,
55,54,60,59,58,62,61,63
};
/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */
/* IMPORTANT: these are only valid for 8-bit data precision! */
const TUint8 bits_dc_luminance[17] =
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
const TUint8 val_dc_luminance[] =
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
const TUint8 bits_dc_chrominance[17] =
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
const TUint8 val_dc_chrominance[] =
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
const TUint8 bits_ac_luminance[17] =
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
const TUint8 val_ac_luminance[] =
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
};
const TUint8 bits_ac_chrominance[17] =
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
const TUint8 val_ac_chrominance[] =
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
};
const TUint8 KExifHeader[] = "Exif";
class THuffman
{
public:
TInt8 iLength[ 256 ];
TUint8 iSymbol[ 256 ];
TUint8 iSearch[ 65536 ];
};
CJpRotate* CJpRotate::NewL( RFs& aFs, RFile* aSaveFile, TInt aSaveBufSize )
{
CJpRotate* self = NewLC( aFs, aSaveFile, aSaveBufSize );
CleanupStack::Pop( self );
return self;
}
CJpRotate* CJpRotate::NewLC( RFs& aFs, RFile* aSaveFile, TInt aSaveBufSize )
{
CJpRotate* self = new( ELeave )CJpRotate( aFs, aSaveFile, aSaveBufSize );
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
CJpRotate::~CJpRotate()
{
delete iHuffman[ 0 ];
delete iHuffman[ 1 ];
delete iHuffman[ 2 ];
delete iHuffman[ 3 ];
delete iSaveHuffman[ 0 ];
delete iSaveHuffman[ 1 ];
delete iSaveHuffman[ 2 ];
delete iSaveHuffman[ 3 ];
delete iQt[ 0 ];
delete iQt[ 1 ];
delete iQt[ 2 ];
delete iQt[ 3 ];
if( iOwnBuffer )
{
delete iBuffer;
}
delete iSaveBuf;
iBasicBlock.Reset();
if( &iSaveFile != NULL )
{
iSaveFile.Flush();
iSaveFile.Close();
}
}
CJpRotate::CJpRotate( RFs& aFs, RFile* aSaveFile, TInt aSaveBufSize )
: iCancelled(EFalse)
, iFs( aFs )
, iSaveFile( *aSaveFile )
, iSaveBufSize( aSaveBufSize )
{
}
void CJpRotate::ConstructL()
{
}
void CJpRotate::SetCallBack( MJpRotateCallBack* aCallBack )
{
iCallBack = aCallBack;
}
void CJpRotate::Cancel()
{
iCancelled = ETrue;
}
void CJpRotate::RotateL( const TFileName& aFile, TBool aRotate, TBool aFlip, TBool aMirror )
{
iCancelled = EFalse;
if (iBuffer)
{
delete iBuffer;
iBuffer = NULL;
}
RFile file;
TInt err = file.Open( iFs, aFile, EFileRead | EFileShareReadersOnly );
if (KErrNone != err)
{
User::LeaveIfError( file.Open( iFs, aFile, EFileRead | EFileShareAny ) );
}
CleanupClosePushL( file );
TInt size;
file.Size( size );
iOwnBuffer = true;
iBuffer = new( ELeave )TUint8[ size + 2 ];
TPtr8 ptr( iBuffer, size );
file.Read( ptr );
CleanupStack::PopAndDestroy( 1 ); // file
RotateL( ptr, aRotate, aFlip, aMirror );
}
void CJpRotate::RotateL( const TPtrC8& aData, TPtrC8& aTarget, TBool aRotate, TBool aFlip, TBool aMirror )
{
iOwnBuffer = false;
RotateL( aData, aRotate, aFlip, aMirror );
aTarget.Set( iSaveBuf, iSaveBufPos );
}
void CJpRotate::RotateL( const TPtrC8& aData, TBool aRotate, TBool aFlip, TBool aMirror )
{
iFlip = aFlip;
iMirror = aMirror;
iRotate = aRotate;
if( iRotate )
{
if( iFlip )
{
iFlip = false;
}
else
{
iFlip = true;
}
}
iBuffer = (TUint8*)aData.Ptr();
iSaveBuf = new( ELeave )TUint8[ iSaveBufSize ];
//Mem::FillZ( iSaveBuf, iS );
iSaveByte = 0;
iSaveBufPos = 0;
iSaveBufBitPos = 0;
// if used again, all variables should be cleared
iBuf = 0;
iBufBits = 0;
delete iHuffman[ 0 ];
delete iHuffman[ 1 ];
delete iHuffman[ 2 ];
delete iHuffman[ 3 ];
iHuffman[ 0 ] = 0;
iHuffman[ 1 ] = 0;
iHuffman[ 2 ] = 0;
iHuffman[ 3 ] = 0;
//CreateDefaultHuffmanL();
iQt[ 0 ] = 0;
iQt[ 1 ] = 0;
iQt[ 2 ] = 0;
iQt[ 3 ] = 0;
TBool moreChunks = ETrue;
TBool possibleChunk = EFalse;
while( moreChunks )
{
if (iCancelled)
{
return;
}
TUint8 b = iBuffer[ iBufPos++ ];
if( possibleChunk )
{
possibleChunk = EFalse;
switch( b )
{
case 0xd8: // start of image
{
WriteSaveBuffer( (TUint16)0xffd8 );
break;
}
case 0xe0: // JFIF application segment
{
WriteSaveBuffer( (TUint16)0xffe0 );
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
WriteSaveBuffer( (TUint16)l );
WriteSaveBuffer( iBuffer + iBufPos, l-2 );
iBufPos += l-2;
break;
}
case 0xe1: // APP1 segment, possible EXIF
{
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ];
iBufPos += 2;
l -= 2;
// Check that there is "Exif" header in the data
const TUint8* pos = &iBuffer[ iBufPos ];
if (Mem::Compare(pos, 2, &KExifHeader[0], 2 ))
{
iBufPos += l;
break;
}
iBufPos += 6;
l -= 6;
iExifData = iBuffer + iBufPos;
iExifDataLength = l;
CExifParser* p = CExifParser::NewLC();
TRAP_IGNORE(p->ParseL( TPtrC8( iExifData, iExifDataLength ) ););
TPtrC8 thumb( p->ThumbData() );
TPtrC8 saveThumb( 0,0 );
TUint8* thumbPtr = 0;
if( iRotate )
{
//
// swap width and height ( if exists )
//
if( p->TagExist( CExifParser::ESubIfd, 0xa002 ) &&
p->TagExist( CExifParser::ESubIfd, 0xa003 ) )
{
TUint16 width = p->TagValue( CExifParser::ESubIfd, 0xa002 );
TUint16 height = p->TagValue( CExifParser::ESubIfd, 0xa003 );
p->DeleteTag( CExifParser::ESubIfd, 0xa002 );
p->DeleteTag( CExifParser::ESubIfd, 0xa003 );
p->AddTagL( CExifParser::ESubIfd, 0xa002, height );
p->AddTagL( CExifParser::ESubIfd, 0xa003, width );
}
}
//
// Rotate thumbnail ( if exists )
//
if( thumb.Ptr() )
{
CJpRotate* r = CJpRotate::NewLC( iFs, NULL, 0x10000 );
TPtrC8 target;
r->RotateL( thumb, target, aRotate, aFlip, aMirror );
thumbPtr = new( ELeave )TUint8[ target.Length() ];
Mem::Copy( thumbPtr, target.Ptr(), target.Length() );
saveThumb.Set( thumbPtr, target.Length() );
CleanupStack::PopAndDestroy( r );
CleanupStack::PushL( thumbPtr );
}
TPtrC8 exif = p->SaveL( saveThumb );
WriteSaveBuffer( (TUint16)0xffe1 ); // APP1 segment
TUint16 l2 = exif.Length() + 6 + 2; // +header+tagsize
WriteSaveBuffer( l2 );
// exif header is 6 bytes ( 45 78 69 66 00 00 "Exif.." )
WriteSaveBuffer( (TUint8)0x45 );
WriteSaveBuffer( (TUint8)0x78 );
WriteSaveBuffer( (TUint8)0x69 );
WriteSaveBuffer( (TUint8)0x66 );
WriteSaveBuffer( (TUint8)0x00 );
WriteSaveBuffer( (TUint8)0x00 );
WriteSaveBuffer( exif.Ptr(), exif.Length() );
if( thumbPtr )
{
CleanupStack::PopAndDestroy( thumbPtr );
}
CleanupStack::PopAndDestroy( p );
iBufPos += l;
break;
}
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe8:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
{
WriteSaveBuffer( (TUint8)0xff );
WriteSaveBuffer( (TUint8)b );
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
WriteSaveBuffer( (TUint16)l );
WriteSaveBuffer( iBuffer + iBufPos, l-2 );
iBufPos += l-2;
break;
}
case 0xdb: // Quantization table
{
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
WriteSaveBuffer( (TUint16)0xffdb );
WriteSaveBuffer( (TUint16)l );
TInt nqt = l / 65;
TInt i;
// one or more quantization table
for( i=0; i<nqt; i++ )
{
TUint8 t = iBuffer[ iBufPos++ ];
TInt n = t & 15;
WriteSaveBuffer( t );
// not used:
// TInt precision = n >> 4;
if (iQt[n])
{
delete iQt[ n ];
iQt[ n ] = NULL;
}
iQt[ n ] = new( ELeave )TUint8[ 64 ];
TInt j;
for( j=0; j<64; j++ )
{
iQt[ n ][ j ] = iBuffer[ iBufPos++ ];
}
TUint8 qt[ 64 ];
ConvertQuants( iQt[ n ], qt );
WriteSaveBuffer( qt, 64 );
}
break;
}
case 0xc0: // start of frame ( SOF )
{
TInt length = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
WriteSaveBuffer( (TUint16)0xffc0 );
WriteSaveBuffer( (TUint16)length );
// not used:
TUint8 precision = iBuffer[ iBufPos ];
WriteSaveBuffer( precision );
iBufPos++;
// height
TUint16 h = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
iData.iSize.iHeight = h;
// width
TUint16 w = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
iData.iSize.iWidth = w;
if( iRotate )
{
WriteSaveBuffer( w );
WriteSaveBuffer( h );
}
else
{
WriteSaveBuffer( h );
WriteSaveBuffer( w );
}
// component data
iNumComponents = iBuffer[ iBufPos++ ];
WriteSaveBuffer( (TUint8)iNumComponents );
TInt i;
for( i=0; i<iNumComponents; i++ )
{
TUint8 comp = iBuffer[ iBufPos++ ] - 1;
TUint8 samplingFactor = iBuffer[ iBufPos++ ];
TUint8 quantizationTable = iBuffer[ iBufPos++ ];
iComponent[ comp ].iXFactor = samplingFactor >> 4;
iComponent[ comp ].iYFactor = samplingFactor & 15;
iComponent[ comp ].iQuantTable = quantizationTable;
comp++;
if( iRotate )
{
samplingFactor = samplingFactor/16 + ( samplingFactor & 15 )*16;
}
WriteSaveBuffer( comp );
WriteSaveBuffer( samplingFactor );
WriteSaveBuffer( quantizationTable );
}
TInt xBlocks = 0;
TInt yBlocks = 0;
for( i=0; i<iNumComponents; i++ )
{
TInt x = iComponent[ i ].iXFactor;
TInt y = iComponent[ i ].iYFactor;
if( x > xBlocks )
{
xBlocks = x;
}
if( y > yBlocks )
{
yBlocks = y;
}
}
iData.iBlockSize.iWidth = xBlocks * 8;
iData.iBlockSize.iHeight = yBlocks * 8;
TSize size = iData.iSize;
size.iWidth /= iData.iBlockSize.iWidth;
size.iHeight /= iData.iBlockSize.iHeight;
if( iData.iSize.iWidth & ( iData.iBlockSize.iWidth - 1 ) )
{
size.iWidth++;
}
if( iData.iSize.iHeight & ( iData.iBlockSize.iHeight - 1 ) )
{
size.iHeight++;
}
iData.iSizeInBlocks = size;
break;
}
case 0xc1:
{
// Extended sequential Jpeg, not supported
User::Leave( KErrNotSupported );
break;
};
case 0xc2:
{
// Progressive DCT jpeg, not supported
User::Leave( KErrNotSupported );
break;
};
case 0xc3:
{
// Lossless ( sequential ) Jpeg, not supported
User::Leave( KErrNotSupported );
break;
};
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf:
{
User::Leave( KErrNotSupported );
break;
};
case 0xc4: // huffman table
{
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
//
// Write default huffman tables
//
WriteSaveBuffer( (TUint16)0xffc4 );
TUint16 saveLen = 2+1+16+12+1+16+12+1+16+162+1+16+162;
WriteSaveBuffer( saveLen );
WriteSaveBuffer( (TUint8)0 );
WriteSaveBuffer( bits_dc_luminance+1, 16 );
WriteSaveBuffer( val_dc_luminance, 12 );
WriteSaveBuffer( (TUint8)1 );
WriteSaveBuffer( bits_dc_chrominance+1, 16 );
WriteSaveBuffer( val_dc_chrominance, 12 );
WriteSaveBuffer( (TUint8)16 );
WriteSaveBuffer( bits_ac_luminance+1, 16 );
WriteSaveBuffer( val_ac_luminance, 20*8+2 );
WriteSaveBuffer( (TUint8)17 );
WriteSaveBuffer( bits_ac_chrominance+1, 16 );
WriteSaveBuffer( val_ac_chrominance, 20*8+2 );
TInt table = 0;
TInt n = 0;
// one or more huffman tables
while( n<l-2 )
{
THuffman* huff = new( ELeave )THuffman;
table = iBuffer[ iBufPos++ ];
n++;
TUint8 huffSize[ 16 ];
TInt numSymbols = 0;
TInt i;
for( i=0; i<16; i++ )
{
TInt size = iBuffer[ iBufPos++ ];
huffSize[ i ] = size;
numSymbols += size;
n++;
}
for( i=0; i<numSymbols; i++ )
{
TUint8 v = iBuffer[ iBufPos++ ];
huff->iSymbol[ i ] = v;
n++;
}
i = ( table & 16 ) / 8 + ( table & 15 );
// Generate huffman lookup tables ( huffSize, table )
TInt l;
TInt p = 0;
for( l=0; l<16; l++ )
{
for( i=0; i<huffSize[ l ]; i++ )
{
huff->iLength[ p++ ] = l+1;
}
}
TInt code = 0;
huff->iLength[ p ] = 0;
TInt si = huff->iLength[ 0 ];
TInt lastP = p;
TInt hc[ 256 ];
p = 0;
while( huff->iLength[ p ] )
{
while( huff->iLength[ p ] == si )
{
hc[ p++ ] = code++;
}
code *= 2;
si++;
}
l = 65536;
for( i=lastP-1; i>=0; i-- )
{
TInt t = 16 - huff->iLength[ i ];
TInt k = hc[ i ] * ( 1 << t );
TInt j;
for( j=k; j<l; j++ )
{
huff->iSearch[ j ] = i;
}
l = k;
}
i = ( table & 16 ) / 8 + ( table & 15 );
delete iHuffman[ i ];
iHuffman[ i ] = huff;
}
break;
}
//
case 0xda: // start of scan ( SOS )
{
// write restart interval before SOS
/*
WriteSaveBuffer( (TUint16)0xffdd );
WriteSaveBuffer( (TUint16)0x0004 );
WriteSaveBuffer( (TUint16)1 ); // reset every (1) macroblock
*/
// SOS now:
WriteSaveBuffer( (TUint16)0xffda );
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
WriteSaveBuffer( (TUint16)l );
TInt c1 = 0;
TInt c2 = 0;
TInt c3 = 0;
TInt numComponents = iBuffer[ iBufPos++ ];
WriteSaveBuffer( (TUint8)3 );
TInt i;
for( i=0; i<numComponents; i++ )
{
TInt componentId = iBuffer[ iBufPos++ ];
TInt componentHt = iBuffer[ iBufPos++ ];
}
WriteSaveBuffer( (TUint8)1 ); // component 1
WriteSaveBuffer( (TUint8)0 );
WriteSaveBuffer( (TUint8)2 ); // component 2
WriteSaveBuffer( (TUint8)17 );
WriteSaveBuffer( (TUint8)3 ); // component 3
WriteSaveBuffer( (TUint8)17 );
WriteSaveBuffer( (TUint8)0 );
WriteSaveBuffer( (TUint8)63 ); // dctsize - 1
WriteSaveBuffer( (TUint8)0 );
iBufPos += 3;
//
// Create default huffman tables for save
//
TSHuffman* huff = new( ELeave )TSHuffman;
CreateSaveHuffmanL( huff, bits_dc_luminance, val_dc_luminance );
iSaveHuffman[ 0 ] = huff;
huff = new( ELeave )TSHuffman;
CreateSaveHuffmanL( huff, bits_dc_chrominance, val_dc_chrominance );
iSaveHuffman[ 1 ] = huff;
huff = new( ELeave )TSHuffman;
CreateSaveHuffmanL( huff, bits_ac_luminance, val_ac_luminance );
iSaveHuffman[ 2 ] = huff;
huff = new( ELeave )TSHuffman;
CreateSaveHuffmanL( huff, bits_ac_chrominance, val_ac_chrominance );
iSaveHuffman[ 3 ] = huff;
// decode
TInt bw = iData.iSizeInBlocks.iWidth;
TInt bh = iData.iSizeInBlocks.iHeight;
TInt bx;
TInt by;
for( by=0; by<bh; by++ )
{
for( bx=0; bx<bw; bx++ )
{
if( iRst )
{
// reset marker zeroes DC-values
c1 = 0;
c2 = 0;
c3 = 0;
iRst = EFalse;
}
TInt i;
// go fast trough all huffman data
iCurrentHuffman = 0;
iCurrentQt = iQt[ 0 ];
// Y-component
TInt n = iComponent[ 0 ].iXFactor * iComponent[ 0 ].iYFactor;
iDct[ 0 ] = c1;
for( i=0; i<n; i++ )
{
DecodeBlock2L();
}
c1 = iDct[ 0 ];
iCurrentHuffman = 1;
iCurrentQt = iQt[ 1 ];
// U-component
n = iComponent[ 1 ].iXFactor * iComponent[ 1 ].iYFactor;
iDct[ 0 ] = c2;
for( i=0; i<n; i++ )
{
DecodeBlock2L();
}
c2 = iDct[ 0 ];
iCurrentSaveHuffman = iSaveHuffman[ 1 ];
// V-component
n = iComponent[ 1 ].iXFactor * iComponent[ 1 ].iYFactor;
iDct[ 0 ] = c3;
for( i=0; i<n; i++ )
{
DecodeBlock2L();
}
c3 = iDct[ 0 ];
// take care of RST0's leftover bits:
if( iRst )
{
TInt goodBits = ( iBufBits >> 3 ) << 3;
TInt badBits = iBufBits - goodBits;
iBuf <<= badBits;
iBufBits = goodBits;
}
}
}
moreChunks = EFalse;
SaveBlocks();
// flush last bits to save buffer
if( iSaveBufBitPos )
{
WriteBits( 0, 8-iSaveBufBitPos );
}
// EOI
//iSaveBuf[ iSaveBufPos++ ] = 0xff;
//iSaveBuf[ iSaveBufPos++ ] = 0xd9;
WriteSaveBuffer( (TUint16)0xffd9 );
FlushSaveBuf();
break;
}
case 0xd9: // end of image ( EOI )
{
// not really used for anything
// will exit if picture data is read.
break;
}
case 0xdd: // define restart interval
{
//TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ];
iBufPos += 2;
//TInt interval = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ];
iBufPos += 2;
break;
}
case 0x00: // escaped 0xff
{
// only comes if file is broken somehow
break;
}
case 0xfe: // jpeg comment
{
TInt l = 256 * iBuffer[ iBufPos ] + iBuffer[ iBufPos + 1 ]; iBufPos += 2;
WriteSaveBuffer( (TUint16)0xfffe );
WriteSaveBuffer( (TUint16)l );
WriteSaveBuffer( iBuffer + iBufPos, l-2 );
iBufPos += l-2;
break;
};
default:
{
// unknown block
break;
}
}
}
else if( b == 255 )
{
possibleChunk = ETrue;
}
}
}
void CJpRotate::BufFwd( TInt aBits )
{
iBuf <<= aBits;
iBufBits -= aBits;
}
TInt CJpRotate::BufBits( TInt aBits )
{
if( aBits == 0 ) return 0;
while( iBufBits < aBits )
{
BufLoad8();
}
TInt val = iBuf >> ( 32-aBits );
BufFwd( aBits );
return val;
}
TInt CJpRotate::Buf16()
{
while( iBufBits < 16 )
{
BufLoad8();
}
TInt val = iBuf >> ( 32-16 );
return val;
}
void CJpRotate::BufLoad8()
{
TInt v;
v = iBuffer[ iBufPos++ ];
if( v == 255 )
{
v = iBuffer[ iBufPos++ ]; // escaped 0xff ?
if( v == 0 )
{
v = 255;
}
else if( v == 0xd9 ) // EOI
{
iEOF = true;
v = 0;
}
else
{
// here we have probably discarded a RST0 or similar
v = iBuffer[ iBufPos++ ];
iRst = ETrue;
}
}
v <<= ( 24 - iBufBits );
iBuf |= v;
iBufBits += 8;
}
void CJpRotate::DecodeBlockL( TInt aDc )
{
//
// Dummy version of block decode
// only traverses through huffman data
// and collects DC-values
//
TInt16 vals[ 64 ];
TUint16 bits[ 64 ];
TUint8 lens[ 64 ];
Mem::FillZ( vals, 64*2 );
Mem::FillZ( bits, 64*2 );
Mem::FillZ( lens, 64 );
//for( TInt i=0; i<64; i++ ) lens[ i ] = 255;
THuffman* h = iHuffman[ iCurrentHuffman ];
TInt k;
for( k=0; k<64; k++ )
{
TInt v = Buf16();
TInt index = h->iSearch[ v ];
TInt symbol = h->iSymbol[ index ];
BufFwd( h->iLength[ index ] );
TInt nullCount = 0;
if( k>0 )
{
nullCount = symbol >> 4;
symbol &= 15;
}
v = BufBits( symbol );
TUint16 currentBits = v;
if( v < ( 1 << ( symbol-1 ) ) )
{
v += 1 - ( 1 << symbol );
}
if( k )
{
if( nullCount==0 && v==0 )
{
k = 64;
break;
}
else if( nullCount==15 && v==0 )
{
k += 15;
}
else
{
k += nullCount;
if( k > 63 ) break;
TInt pos = k;
if( iRotate ) pos = KReZig[ pos ];
lens[ pos ] = symbol;
vals[ pos ] = v;
bits[ pos ] = currentBits;
}
}
else
{
h = iHuffman[ iCurrentHuffman+2 ]; // DC -> AC huffman
lens[ 0 ] = symbol;
vals[ 0 ] = aDc / iCurrentQt[ 0 ];
//vals[ 0 ] = aDc;
bits[ 0 ] = currentBits;
}
}
TInt count;
for( count=63; count>=0; count-- )
{
if( lens[ count ] != 0 ) break;
}
if( count < 0 ) count = 0;
TInt nullCount = 0;
//
// Write DC
//
TInt value = vals[ 0 ];
bool minus = false;
if( value < 0 )
{
minus = true;
value = -value;
}
TInt v = value;
TInt vl = 0;
while( v )
{
v >>= 1;
vl++;
}
if( minus )
{
TInt a = ( 1 << vl ) - 1;
value ^= a;
}
WriteHuffmanL( vl );
WriteBits( value, vl );
// DC -> AC huffman
if( iCurrentSaveHuffman == iSaveHuffman[ 0 ] )
{
iCurrentSaveHuffman = iSaveHuffman[ 2 ];
}
else if( iCurrentSaveHuffman == iSaveHuffman[ 1 ] )
{
iCurrentSaveHuffman = iSaveHuffman[ 3 ];
}
//
// Write AC
//
for( k=1; k<=count; k++ )
{
if( vals[ k ] == 0 )
{
nullCount++;
if( nullCount == 16 )
{
// write nullCount 15 with value 0
WriteHuffmanL( 15 * 16 );
nullCount = 0;
}
}
else
{
// write bits & nullCount huffman coded
TUint8 neg = 0;
TInt p = KZigZag[ k ];
if( p & 8 && iMirror ) neg = 1;
if( p & 1 && iFlip ) neg ^= 1;
TUint16 b = bits[ k ];
TInt l = lens[ k ];
TInt v = vals[ k ];
if( neg )
{
if( v>0 )
{
b = v ^ ( ( 1 << l ) - 1 );
}
else
{
b = -v;
}
}
WriteHuffmanL( l + nullCount * 16 );
WriteBits( b,l );
nullCount = 0;
}
}
if( count != 63 )
{
WriteHuffmanL( 0 );
}
}
void CJpRotate::DecodeBlock2L()
{
THuffman* h = iHuffman[ iCurrentHuffman ];
TInt k;
TJpegBasicBlock bl;
bl.iOffset = iBufPos;
bl.iBuf = iBuf;
bl.iBufBits = iBufBits;
for( k=0; k<64; k++ )
{
TInt v = Buf16();
TInt index = h->iSearch[ v ];
TInt symbol = h->iSymbol[ index ];
BufFwd( h->iLength[ index ] );
TInt nullCount = 0;
if( k>0 )
{
nullCount = symbol >> 4;
symbol &= 15;
}
v = BufBits( symbol );
if( v < ( 1 << ( symbol-1 ) ) )
{
v += 1 - ( 1 << symbol );
}
if( k )
{
if( nullCount==0 && v==0 )
{
k = 64;
break;
}
else if( nullCount==15 && v==0 )
{
k += 15;
}
else
{
k += nullCount;
}
}
else
{
iDct[ 0 ] += v * iCurrentQt[ 0 ];
h = iHuffman[ iCurrentHuffman+2 ];
}
}
bl.iDc = iDct[ 0 ];
User::LeaveIfError(iBasicBlock.Append( bl ));
//Idct();
}
const TJpegData& CJpRotate::Info()
{
return iData;
}
TPtrC8 CJpRotate::ExifData()
{
return TPtrC8( iExifData, iExifDataLength );
}
void CJpRotate::WriteHuffmanL( TInt aValue )
{
TInt code = iCurrentSaveHuffman->iCode[ aValue ];
TInt len = iCurrentSaveHuffman->iLength[ aValue ];
if( aValue != 0 && len == 0 )
{
//
// The value which we tried to write doesn't exist in
// the huffman table. Therefore the output would be
// corrupted. Thus the leave.
// actually this should not happen ever.
//
User::Leave( KErrCorrupt );
}
WriteBits( code, len );
}
void CJpRotate::WriteBits( TUint32 aValue, TInt aNumBits )
{
aValue &= ( ( 1 << aNumBits ) - 1 );
while( aNumBits > 0 )
{
TInt bitroom = 8 - iSaveBufBitPos;
//iSaveBuf[ iSaveBufPos ] |= ( ( aValue << ( 24+bitroom-aNumBits ) ) >> 24 );
iSaveByte |= ( ( aValue << ( 24+bitroom-aNumBits ) ) >> 24 );
if( aNumBits < bitroom )
{
iSaveBufBitPos += aNumBits;
}
else
{
iSaveBufBitPos += bitroom;
}
if( iSaveBufBitPos == 8 )
{
iSaveBufBitPos = 0;
iSaveBuf[ iSaveBufPos ] = iSaveByte;
if( iSaveByte == 255 )
{
iSaveBufPos++;
if( iSaveBufPos == iSaveBufSize )
{
FlushSaveBuf();
}
iSaveBuf[ iSaveBufPos ] = 0; // 255,0 = 255 ( escaped 255 )
}
iSaveByte = 0;
iSaveBufPos++;
if( iSaveBufPos == iSaveBufSize )
{
FlushSaveBuf();
}
}
aNumBits -= bitroom;
}
}
void CJpRotate::CreateSaveHuffmanL( TSHuffman* aHuffman, const TUint8* aBits, const TUint8* aVal )
{
TInt huffSize[ 16 ];
TUint32 huffCode[ 256 ];
TInt huffValue[ 256 ];
TInt huffLength[ 256 ];
TInt numSymbols = 0;
TInt i;
for( i=0; i<16; i++ )
{
TInt size = aBits[ i+1 ];
huffSize[ i ] = size;
numSymbols += size;
}
for( i=0; i<numSymbols; i++ )
{
huffValue[ i ] = aVal[ i ];
}
TInt l;
TInt p = 0;
for( i=0; i<256; i++ ) huffLength[ i ] = -1;
for( l=0; l<16; l++ )
{
for( i=0; i<huffSize[ l ]; i++ )
{
huffLength[ p++ ] = l+1;
}
}
TInt code = 0;
huffLength[ p ] = 0;
TInt si = huffLength[ 0 ];
p = 0;
while( huffLength[ p ] )
{
while( huffLength[ p ] == si )
{
huffCode[ p++ ] = code++;
}
code *= 2;
si++;
}
for( i=0; i<numSymbols; i++ )
{
TInt v = huffValue[ i ];
aHuffman->iLength[ v ] = huffLength[ i ];
aHuffman->iCode[ v ] = huffCode[ i ];
}
}
void CJpRotate::ConvertQuants( TUint8* aSrc, TUint8* aTgt )
{
if( ! iRotate )
{
Mem::Copy( aTgt, aSrc, 64 );
return;
}
for( TInt i=0; i<64; i++ )
{
aTgt[ KReZig[ i ] ] = aSrc[ i ];
}
}
void CJpRotate::WriteSaveBuffer( const TUint8* aSrc, TInt aBytes )
{
TInt pos = 0;
while( aBytes )
{
TInt bytes = aBytes;
if( iSaveBufPos + bytes > iSaveBufSize )
{
bytes = iSaveBufSize - iSaveBufPos;
}
Mem::Copy( iSaveBuf + iSaveBufPos, aSrc + pos, bytes );
iSaveBufPos += bytes;
if( iSaveBufPos == iSaveBufSize )
{
FlushSaveBuf();
}
aBytes -= bytes;
pos += bytes;
}
}
void CJpRotate::WriteSaveBuffer( TUint8 aValue )
{
iSaveBuf[ iSaveBufPos++ ] = aValue;
if( iSaveBufPos == iSaveBufSize )
{
FlushSaveBuf();
}
}
void CJpRotate::WriteSaveBuffer( TUint16 aValue )
{
WriteSaveBuffer( (TUint8) ( aValue / 256 ) );
WriteSaveBuffer( (TUint8) ( aValue & 255 ) );
}
void CJpRotate::SaveBlocks()
{
TInt yBlocks = iComponent[ 0 ].iXFactor * iComponent[ 0 ].iYFactor;
TInt uBlocks = iComponent[ 1 ].iXFactor * iComponent[ 1 ].iYFactor;
TInt vBlocks = iComponent[ 2 ].iXFactor * iComponent[ 2 ].iYFactor;
TInt blocks = yBlocks + uBlocks + vBlocks;
TInt blockOffset[ 16 ];
TInt x;
TInt y;
TInt i = 0;
TInt b = 0;
for( TInt comp = 0; comp<iNumComponents; comp++ )
{
TComponent& c = iComponent[ comp ];
for( y=0; y<c.iYFactor; y++ )
{
for( x=0; x<c.iXFactor; x++ )
{
TInt xx = x;
TInt yy = y;
TInt fx = c.iXFactor;
TInt fy = c.iYFactor;
if( iRotate )
{
TInt t = xx; xx = yy; yy = t;
if( iFlip ) yy = fx - 1 - yy;
if( iMirror ) xx = fy - 1 - xx;
t = fx; fx=fy; fy = t;
}
else
{
if( iFlip ) yy = fy - 1 - yy;
if( iMirror ) xx = fx - 1 - xx;
}
TInt v = xx + yy * fx; //1302
//while( v >= n ) v -= n;
//blockOffset[ i++ ] = b + v;
blockOffset[ b+v ] = i++;
}
}
b += c.iXFactor*c.iYFactor;
}
TInt bw = iData.iSizeInBlocks.iWidth;
TInt bh = iData.iSizeInBlocks.iHeight;
TInt yDc = 0;
TInt uDc = 0;
TInt vDc = 0;
TInt count = 0;
TInt blockXAdd;
TInt blockYAdd;
TInt blockPos;
if( iRotate )
{
if( iMirror )
{
if( iFlip )
{
blockPos = blocks * ( bw * bh - 1 );
blockXAdd = -blocks * bw;
blockYAdd = blocks * bw * bh - blocks;
}
else
{
blockPos = blocks * bw * ( bh - 1 );
blockXAdd = -blocks * bw;
blockYAdd = blocks * bw * bh + blocks;
}
}
else
{
if( iFlip )
{
blockPos = blocks * ( bw - 1 );
blockXAdd = blocks * bw;
blockYAdd = -blocks * bw * bh - blocks;
}
else
{
blockPos = 0;
blockXAdd = blocks * bw;
blockYAdd = -blocks * ( bw * bh - 1 );
}
}
TInt t = bw; bw = bh; bh = t;
}
else
{
if( iMirror )
{
if( iFlip )
{
blockPos = blocks * ( bw * bh - 1 );
blockXAdd = -blocks;
blockYAdd = 0;
}
else
{
blockPos = blocks * ( bw-1 );
blockXAdd = -blocks;
blockYAdd = bw * blocks * 2;
}
}
else
{
if( iFlip )
{
blockPos = blocks * bw * ( bh-1 );
blockXAdd = blocks;
blockYAdd = -blocks*bw*2;
}
else
{
blockPos = 0;
blockXAdd = blocks;
blockYAdd = 0;
}
}
}
TInt numMacroBlocks = bw*bh;
for( y=0; y<bh; y++ )
{
for( x=0; x<bw; x++ )
{
iCurrentQt = iQt[ 0 ];
TInt i = 0;
TInt n;
for( n=0; n<yBlocks; n++ )
{
TInt bo = blockOffset[ i++ ];
TJpegBasicBlock& bl = iBasicBlock[ blockPos + bo ];
iBuf = bl.iBuf;
iBufPos = bl.iOffset;
iBufBits = bl.iBufBits;
iCurrentHuffman = 0;
iCurrentSaveHuffman = iSaveHuffman[ 0 ];
DecodeBlockL( bl.iDc - yDc );
yDc = bl.iDc;
}
iCurrentQt = iQt[ 1 ];
for( n=0; n<uBlocks; n++ )
{
TJpegBasicBlock& bl = iBasicBlock[ blockPos + blockOffset[ i++ ] ];
iBuf = bl.iBuf;
iBufPos = bl.iOffset;
iBufBits = bl.iBufBits;
iCurrentHuffman = 1;
iCurrentSaveHuffman = iSaveHuffman[ 1 ];
DecodeBlockL( bl.iDc - uDc );
uDc = bl.iDc;
}
for( n=0; n<vBlocks; n++ )
{
TJpegBasicBlock& bl = iBasicBlock[ blockPos + blockOffset[ i++ ] ];
iBuf = bl.iBuf;
iBufPos = bl.iOffset;
iBufBits = bl.iBufBits;
iCurrentHuffman = 1;
iCurrentSaveHuffman = iSaveHuffman[ 1 ];
DecodeBlockL( bl.iDc - vDc );
vDc = bl.iDc;
}
//
// Rst ( 0xFFD0..0xFFD7 ) after each macroblock
//
/*
if( iSaveBufBitPos )
{
WriteBits( 0,8-iSaveBufBitPos );
}
WriteSaveBuffer( (TUint16)( 0xffd0 + (count & 7 ) ) );
yDc = 0;
uDc = 0;
vDc = 0;
count++;
*/
blockPos += blockXAdd;
if( iCallBack )
{
iCallBack->JpRotateStatus( count, numMacroBlocks );
}
}
blockPos += blockYAdd;
}
//RDebug::Print( _L("last access block :%d"), mx );
}
void CJpRotate::FlushSaveBuf()
{
if( &iSaveFile == NULL ) return; // no flushing without file
TPtr8 ptr( iSaveBuf, iSaveBufPos );
ptr.SetLength( iSaveBufPos );
iSaveFile.Write( ptr );
iSaveBufPos = 0;
}