imagingmodules/exiflib/src/ExifEndian.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 09:22:31 +0200
changeset 0 469c91dae73b
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2003, 2004 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "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:
*
* Description:  Exif data parser, that can parse both Little and Big endian 
*                formats
*
*/


// INCLUDE FILES
#include "ExifEndian.h"
#include "ExifCommon.h"

// ============================ CLASS CExifEndianBase ==========================
// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CExifEndianBase::CExifEndianBase
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CExifEndianBase::CExifEndianBase()
    {
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CExifEndianBase::ConstructL()
    {
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::NewBaseL
// Two-phased constructor. Instantiates and returns relevant derived class.
// -----------------------------------------------------------------------------
//
CExifEndianBase* CExifEndianBase::NewBaseL( 
    const TUint8* aExifDataPtr, 
    TUint aDataLength, 
    TBool aIsExif )
    {
    // 
    // Parse header. 
    // Find SOI (FFD8 )
    // ptr = SOI pos.
    // Then check the following 10 bytes:
    // APP1 Marker (FFE1) + APP1 Length (no check) + Identifier ("Exif"00) + 
    // Pad (00) (no check)

    //TUint8* dataStartPtr = CONST_CAST( TUint8*, aExifData.Ptr() ); 
    if ( !aExifDataPtr )
        {
        User::Leave( KErrGeneral );
        }
    TUint8* ptr = CONST_CAST( TUint8*, aExifDataPtr ) - 1;
    TUint8 *pE = CONST_CAST( TUint8*, aExifDataPtr ) + aDataLength; 

    // Locate the SOI marker.
    do
        {
        if ( ptr == pE )
            {
            User::Leave( KErrCorrupt );
            }
        } 
    while ( *++ptr != KMarkerStart );
    
    if ( *( ptr + 1 ) != KSoiEnd )
        {
        User::Leave( KErrCorrupt );
        }
    // ptr is now at SOI.

    // If original is Exif, check whether it is Little- or Big-endian.
    // Otherwise create Little-endian reader.
    if ( aIsExif )
        {
        // Locate the APP1 marker
        do
            {
            do
                {
                if ( ptr == pE )
                    {
                    User::Leave( KErrCorrupt );
                    }
                } 
            while ( *++ptr != KMarkerStart );
            } 
        while ( *( ptr + 1 ) != KApp1End );
        // ptr is now at APP1
        // Skip App1 length.
        ptr += 4;

        // Check the APP1 and Exif header.
        if ( TExifCommon::Uint32L( ptr ) != KExifIdentifierRev ) 
            {
            User::Leave( KErrCorrupt );
            }
        ptr += 4;

        // Padding 0 after 8 bytes of data. No need to check, skip.
        //if ( ( *ptr ) != 0)
        //    {
        //    User::Leave( KErrCorrupt );
        //    }
        ptr += 2;

        // Check if little- or big- endian, and create the reader.
        if ( TExifCommon::Uint16L( ptr ) == KLittleEndian )
            {
            return CExifLittleEndian::NewL( aExifDataPtr, ptr, pE, aIsExif );
            }
        else if ( TExifCommon::Uint16L( ptr ) == KBigEndian )
            {
            return CExifBigEndian::NewL( aExifDataPtr, ptr, pE, aIsExif );
            }
        else // None of the formats match!!
            {
            User::Leave( KErrCorrupt );
            }
        }
    else
        {
        return CExifLittleEndian::NewL( aExifDataPtr, ptr, pE, aIsExif );
        }

    return NULL;
    }

// Destructor
CExifEndianBase::~CExifEndianBase()
    {
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::CopyBufferL
// Copies the data from the location, which starts and ends with the given 
// Jpeg Markers.
// -----------------------------------------------------------------------------
//
void CExifEndianBase::CopyBufferL( 
    TUint16 aStartMarker, 
    TUint16 aEndMarker, 
    HBufC8*& aDestBuffer, 
    TBool aIncludeEnd )
    {
    TUint32 startOffset = 0;
    if ( LocateJpegMarker( aStartMarker, startOffset ) )
        {
        User::Leave( KErrCorrupt );
        }
    TUint32 endOffset = 0;
    if ( LocateJpegMarker( aEndMarker, endOffset ) )
        {
        User::Leave( KErrCorrupt );
        }
    TUint32 size =  endOffset - startOffset;
    if ( aIncludeEnd )
        {
        size += 2;
        }
    aDestBuffer = HBufC8::NewL( size );
    CleanupStack::PushL( aDestBuffer );

    iPosOffset = startOffset;

    CopyBuffer( size, 1 , aDestBuffer );
    CleanupStack::Pop();
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::ReadUint8
// Gets the next 8-bit unsigned integer from the data.
// -----------------------------------------------------------------------------
//
TInt CExifEndianBase::ReadUint8( TUint8& aInt8 )
    {
    if ( iPosOffset + 1 >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    aInt8 = *( iDataStartPtr + iPosOffset );
    ++iPosOffset;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::MoveTo
// Moves to the specified offset.
// -----------------------------------------------------------------------------
//
TInt CExifEndianBase::MoveTo( TUint aOffset )
    {
    if ( iExifStartOffset + aOffset >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    iPosOffset = iExifStartOffset + aOffset;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::Skip
// Moves the current position forward by the given number of bytes.
// -----------------------------------------------------------------------------
//
TInt CExifEndianBase::Skip( TUint aNoBytes )
    {
    if ( iPosOffset + aNoBytes >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    iPosOffset += aNoBytes;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::Locate8
// Locates the offset of the next specified 8-bit unsigned integer 
// -----------------------------------------------------------------------------
//
TInt CExifEndianBase::Locate8( TUint8 aSearch, TUint32& aOffset )
    {
    TUint8 *pBuf = iDataStartPtr + iPosOffset;
    TUint8 *pB = pBuf - 1;
    TUint8* pE = iDataStartPtr + iExifEndOffset;
    do
        {
        if ( pB == pE )
            {
            return KErrCorrupt;
            }
        } 
    while ( *++pB != aSearch );
    aOffset = pB - iDataStartPtr;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::LocateJpegMarker
// Locates the offset of the specified Jpeg Marker, which resides after the 
// given offset.
// -----------------------------------------------------------------------------
//
TInt CExifEndianBase::LocateJpegMarker( 
    TUint16 aMarker, 
    TUint32& aOffset, 
    TUint32 aAfter,
    TUint32 aBefore )
    {
    if ( aAfter < iPosOffset )
        {
        aAfter = iPosOffset;
        }
    TUint8* startPtr = iDataStartPtr + aAfter;
    if ( !aBefore )
        {
        aBefore = iExifEndOffset;
        }
    TUint8* endPtr = iDataStartPtr + aBefore;
    TUint8* pB = TExifCommon::LocateJpegMarkerPtr( 
        aMarker, startPtr, endPtr );
    if ( !pB )
        {
        return KErrCorrupt;
        }
    aOffset = pB - iDataStartPtr;
    return KErrNone;    
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::LocateJpegMarkerFromEnd
// Locates the offset of the last specified Jpeg Marker.
// -----------------------------------------------------------------------------
//
TInt CExifEndianBase::LocateJpegMarkerFromEnd( 
    TUint16 aMarker, 
    TUint32& aOffset,
    TUint32 aAfter,
    TUint32 aBefore )
    {
    if ( aAfter < iPosOffset)
        {
        aAfter = iPosOffset;
        }
    TUint8* startPtr = iDataStartPtr + aAfter;
    if ( !aBefore )
        {
        aBefore = iExifEndOffset;
        }
    TUint8* endPtr = iDataStartPtr + aBefore;
    TUint8* pB = TExifCommon::LocateJpegMarkerPtrFromEnd( aMarker, 
        startPtr, endPtr );
    if ( !pB )
        {
        return KErrCorrupt;
        }
    aOffset = pB - iDataStartPtr;
    return KErrNone;    
    }


// ============================ CLASS CExifLittleEndian ========================
// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CExifLittleEndian::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CExifLittleEndian* CExifLittleEndian::NewL( 
    const TUint8* aDataStartPtr, 
    TUint8* aExifStartPtr, 
    TUint8* aExifEndPtr, 
    TBool aIsExif )
    {
    CExifLittleEndian* self = new( ELeave ) CExifLittleEndian();
    CleanupStack::PushL( self );
    self->ConstructL( CONST_CAST( TUint8*, aDataStartPtr ), aExifStartPtr, 
        aExifEndPtr, aIsExif );
    CleanupStack::Pop();
    return self;
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::CExifLittleEndian
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CExifLittleEndian::CExifLittleEndian()
    {
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CExifLittleEndian::ConstructL( 
    TUint8* aDataStartPtr, 
    TUint8* aExifStartPtr, 
    TUint8* aExifEndPtr, 
    TBool aIsExif )
    {
    if ( !aDataStartPtr || !aExifStartPtr || !aExifEndPtr )
        {
        User::Leave( KErrGeneral );
        }
    iDataStartPtr = aDataStartPtr;
    iExifEndOffset = aExifEndPtr - iDataStartPtr;
    if ( aIsExif )
        {
        iExifStartOffset = aExifStartPtr - iDataStartPtr;
        if ( TExifCommon::Uint16L( aExifStartPtr + 2 ) != KExifDummyRev )
            {
            User::Leave( KErrCorrupt );
            }
        TUint32 ifdOffset = TExifCommon::Uint32L( aExifStartPtr + 4 );
        iPosOffset = iExifStartOffset + ifdOffset; //Locate 0th IFD position
        if ( iPosOffset >= iExifEndOffset )
            {
            User::Leave( KErrCorrupt );
            }
        }
    else
        {
        iPosOffset = aExifStartPtr - iDataStartPtr;
        }
    }

// Destructor
CExifLittleEndian::~CExifLittleEndian()
    {
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::Locate16
// Locates the offset of the next specified 16-bit unsigned integer.
// -----------------------------------------------------------------------------
//
TInt CExifLittleEndian::Locate16( TUint16 aSearch, TUint32& aOffset )
    {
    TUint8* pB = iDataStartPtr + iPosOffset - 1;
    TUint16 atHand = *pB;
    TUint8* pE = iDataStartPtr + iExifEndOffset;
    TInt error = KErrNone;
    do
        {
        if ( pB >= pE )
            {
            return KErrCorrupt;
            }
        TRAP( error, atHand =TExifCommon::Uint16L( ++pB ) );
        } 
    while ( ( atHand != aSearch ) && ( error == KErrNone ) );
    if ( error )
        {
        return error;
        }
    aOffset = pB - iDataStartPtr;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::ReadUint16
// Gets the next 16-bit unsigned integer from the data.
// -----------------------------------------------------------------------------
//
TInt CExifLittleEndian::ReadUInt16( TUint16& aInt16 )
    {
    if ( iPosOffset + 2 >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    
    TRAPD( error, aInt16 = TExifCommon::Uint16L( iDataStartPtr + iPosOffset ) );

    if ( error )
        {
        return error;
        }
    
    iPosOffset += 2;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::ReadUint32
// Gets the next 32-bit unsigned integer from the data.
// -----------------------------------------------------------------------------
//
TInt CExifLittleEndian::ReadUInt32( TUint32& aInt32 )
    {
    if ( iPosOffset + 4 >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    
    TRAPD( error, aInt32 = TExifCommon::Uint32L( iDataStartPtr + iPosOffset ) );
    if ( error )
        {
        return error;
        }
    
    iPosOffset += 4;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::CopyBuffer
// Copies the next specified amount of data to the given descriptor.
// -----------------------------------------------------------------------------
//
TInt CExifLittleEndian::CopyBuffer( 
    TUint32 aCount, 
    TUint8 aWordSize, 
    HBufC8*& aBuffer )
    {
    if ( !aBuffer )
        {
        return KErrCorrupt;
        }
    TUint bufferSize = aCount * aWordSize;
    if ( iPosOffset + bufferSize >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    
    aBuffer->Des().Copy( iDataStartPtr + iPosOffset, bufferSize );

    iPosOffset += bufferSize;

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifLittleEndian::CopyBuffer
// Copies the specified amount of data located in the given offset to the 
// given descriptor.
// -----------------------------------------------------------------------------
//
TInt CExifLittleEndian::CopyBuffer( 
    TUint32 aOffset, 
    TUint32 aCount, 
    TUint8 aWordSize, 
    HBufC8*& aBuffer )
    {
    if ( !aBuffer )
        {
        return KErrCorrupt;
        }
    TUint bufferSize = aCount * aWordSize;
    if ( ( aOffset + iExifStartOffset + bufferSize ) >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    
    TUint8* ptr = iDataStartPtr + iExifStartOffset + aOffset;
    aBuffer->Des().Copy( ptr, bufferSize );

    return KErrNone;
    }


// ============================ CLASS CExifBigEndian ===========================
// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CExifBigEndian::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CExifBigEndian* CExifBigEndian::NewL( 
    const TUint8* aDataStartPtr, 
    TUint8* aExifStartPtr, 
    TUint8* aExifEndPtr, 
    TBool aIsExif )
    {
    CExifBigEndian* self = new( ELeave ) CExifBigEndian();
    CleanupStack::PushL( self );
    self->ConstructL( CONST_CAST( TUint8*, aDataStartPtr ), aExifStartPtr, 
        aExifEndPtr, aIsExif );
    CleanupStack::Pop();
    return self;
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::CExifBigEndian
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CExifBigEndian::CExifBigEndian()
    {
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CExifBigEndian::ConstructL( 
    TUint8* aDataStartPtr, 
    TUint8* aExifStartPtr, 
    TUint8* aExifEndPtr, 
    TBool aIsExif )
    {
    if ( !aDataStartPtr || !aExifStartPtr || !aExifEndPtr )
        {
        User::Leave( KErrGeneral );
        }
    iDataStartPtr = aDataStartPtr;
    iExifEndOffset = aExifEndPtr - iDataStartPtr;
    if ( aIsExif )
        {
        iExifStartOffset = aExifStartPtr - iDataStartPtr;
        if ( TExifCommon::Uint16L( aExifStartPtr + 2 ) != KExifDummy )
            {
            User::Leave( KErrCorrupt );
            }

        TUint8* tmpPtr = aExifStartPtr + 4;
        TUint32 ifdOffset = STATIC_CAST( TUint32,  ( *tmpPtr << 24 ) 
            + ( ( *( tmpPtr + 1 ) ) << 16 ) +  ( ( *( tmpPtr + 2 ) ) << 8 ) 
            + ( *( tmpPtr + 3 ) ) ); 
        iPosOffset = ( iExifStartOffset + ifdOffset ); //Locate 0th IFD pos.
        if ( iPosOffset >= iExifEndOffset )
            {
            User::Leave( KErrCorrupt );
            }
        }
    else
        {
        iPosOffset = aExifStartPtr - iDataStartPtr;
        }
    }

// Destructor
CExifBigEndian::~CExifBigEndian()
    {
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::Locate16
// Locates the offset of the next specified 16-bit unsigned integer.
// -----------------------------------------------------------------------------
//
TInt CExifBigEndian::Locate16( TUint16 aSearch, TUint32& aOffset )
    {
    TUint8* pB = iDataStartPtr + iPosOffset;
    TUint16 atHand = *pB;
    TUint8* pE = iDataStartPtr + iExifEndOffset;
    do
        {
        if ( pB >= pE )
            {
            return KErrCorrupt;
            }
        atHand = STATIC_CAST( TUint16, ( atHand << 8 ) + *++pB );
        } 
    while ( atHand != aSearch );
    aOffset = ( pB - 1 ) - iDataStartPtr;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::ReadUInt16
// Gets the next 16-bit unsigned integer from the data.
// -----------------------------------------------------------------------------
//
TInt CExifBigEndian::ReadUInt16( TUint16& aInt16 )
    {
    if ( iPosOffset + 2 >= iExifEndOffset )
        {
        return KErrCorrupt;
        }

    aInt16 = STATIC_CAST( TUint16,  ( *( iDataStartPtr + iPosOffset ) << 8 ) 
        + *( iDataStartPtr + iPosOffset + 1 ) ); 
    
    iPosOffset += 2;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::ReadUInt32
// Gets the next 32-bit unsigned integer from the data.
// -----------------------------------------------------------------------------
//
TInt CExifBigEndian::ReadUInt32( TUint32& aInt32 )
    {
    if ( iPosOffset + 4 >= iExifEndOffset )
        {
        return KErrCorrupt;
        }

    aInt32 = STATIC_CAST( TUint32,  ( *( iDataStartPtr + iPosOffset ) << 24 ) 
        + ( *( iDataStartPtr + iPosOffset + 1 ) << 16 ) 
        + ( *( iDataStartPtr + iPosOffset + 2 ) << 8 ) 
        + *( iDataStartPtr + iPosOffset + 3 ) ); 

    iPosOffset += 4;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::CopyBuffer
// Copies the next specified amount of data to the given descriptor.
// -----------------------------------------------------------------------------
//
TInt CExifBigEndian::CopyBuffer( 
    TUint32 aCount, 
    TUint8 aWordSize, 
    HBufC8*& aBuffer )
    {
    if ( !aBuffer )
        {
        return KErrCorrupt;
        }
    TInt bufferSize = aCount * aWordSize;
    if ( iPosOffset + bufferSize >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    
    TUint8* posPtr = iDataStartPtr + iPosOffset;
    if ( aWordSize == 1 )
        {
        aBuffer->Des().Copy( posPtr, bufferSize );
        iPosOffset += bufferSize;
        }
    else if ( aWordSize == 2 )
        {    
        TUint8* destPtr = CONST_CAST( TUint8*, aBuffer->Ptr() );
        for( TUint i = 0; i < aCount; ++i)
            {
            destPtr[0] = posPtr[1];
            destPtr[1] = *posPtr;
            posPtr += 2;
            iPosOffset += 2;
            destPtr += 2;
            }
        }
    else
        {    
        TUint8* destPtr = CONST_CAST( TUint8*, aBuffer->Ptr() );
        for( TInt i = 0; i < bufferSize / 4; ++i)
            {
            destPtr[0] = posPtr[3];
            destPtr[1] = posPtr[2];
            destPtr[2] = posPtr[1];
            destPtr[3] = *posPtr;
            posPtr += 4;
            iPosOffset += 4;
            destPtr += 4;
            }
        }
    aBuffer->Des().SetLength( bufferSize );
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifBigEndian::CopyBuffer
// Copies the specified amount of data located in the given offset to the 
// given descriptor.
// -----------------------------------------------------------------------------
//
TInt CExifBigEndian::CopyBuffer( 
    TUint32 aOffset, 
    TUint32 aCount, 
    TUint8 aWordSize, 
    HBufC8*& aBuffer )
    {
    if ( !aBuffer )
        {
        return KErrCorrupt;
        }
    TInt bufferSize = aCount * aWordSize;
    if ( ( aOffset + iExifStartOffset + bufferSize ) >= iExifEndOffset )
        {
        return KErrCorrupt;
        }
    
    TUint8* ptr = iDataStartPtr + iExifStartOffset + aOffset;
    if ( aWordSize == 1 )
        {
        aBuffer->Des().Copy( ptr, bufferSize );
        }
    else if ( aWordSize == 2 )
        {
        TUint8* destPtr = CONST_CAST( TUint8*, aBuffer->Ptr() );
        for( TUint i = 0; i < aCount; ++i)
            {
            destPtr[0] = ptr[1];
            destPtr[1] = *ptr;
            destPtr += 2;
            ptr += 2;
            }
        }
    else
        {
        TUint8* destPtr = CONST_CAST( TUint8*, aBuffer->Ptr() );
        for( TInt i = 0; i < bufferSize / 4; ++i)
            {
            destPtr[0] = ptr[3];
            destPtr[1] = ptr[2];
            destPtr[2] = ptr[1];
            destPtr[3] = *ptr;
            destPtr += 4;
            ptr += 4;
            }
        }
    aBuffer->Des().SetLength( bufferSize );
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CExifEndianBase::CurrentPosOffset
// Returns the current position of decoded data
// -----------------------------------------------------------------------------
//
void CExifEndianBase::CurrentPosOffset( TUint32& aOffset )
    {
    aOffset = iPosOffset;
    }