imaging/imagingplugins/codecs/JPEGCodec/Exif/ifdgeneralentry.cpp
changeset 0 5752a19fdefe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imaging/imagingplugins/codecs/JPEGCodec/Exif/ifdgeneralentry.cpp	Wed Aug 25 12:29:52 2010 +0300
@@ -0,0 +1,693 @@
+// Copyright (c) 2004-2009 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:
+//
+
+#include "ifdgeneral.h"
+#include "ifdgeneralentry.h"
+#include "ExifTagDescriptions.h"
+#include "ExifTagHelper.h"
+#include "ImageUtils.h"
+
+const TUint KAnySize = 0; // For tags that have no upper limit on their content size.
+
+GLDEF_C void Cleanup(TAny *aObject)
+      {
+      User::Free(aObject);
+      }
+ 
+// Sets the value/offset field (four bytes) of this Ifd entry.
+void CIfdGeneralEntry::SetValueOffset(const TUint8* aValueOffset)
+	{
+	/*
+	Need to check the type of data, and store accordingly - see Exif2.2 specification pp.14: 
+	"If the value is < 4 bytes, the value is stored in the 4 byte area starting from the left,
+	i.e. from the lower end of the byte offset.  For example, in big endian format, if the
+	type is SHORT and the value is 1, it is recorded as 00010000.H."
+	*/
+	TUint totalSize = KDataFormatSize[Format()] * ComponentCount();
+	
+	switch(totalSize)
+		{
+		case (sizeof(TUint8)): // 1 byte in length.
+			Mem::Copy(iDirEntryData+KValueOffsetFieldPosition, aValueOffset, sizeof(TUint8));
+			Mem::FillZ(iDirEntryData+KValueOffsetFieldPosition+sizeof(TUint8), KThreeBytes); // Fill remaining 3 bytes with zeros.
+			break;
+		case (sizeof(TUint16)): // 2 bytes in length.
+			{
+			const TUint16* shortValue = reinterpret_cast<const TUint16*>(aValueOffset);
+			Mem::Copy(iDirEntryData+KValueOffsetFieldPosition, shortValue, sizeof(TUint16)); // Fill remaining 2 bytes with zeros.
+			Mem::FillZ(iDirEntryData+KValueOffsetFieldPosition+sizeof(TUint16), sizeof(TUint16));
+			}
+			break;
+		case KThreeBytes: // 3 bytes in length.  Fall through.
+		case (sizeof(TUint32)): // 4 bytes in length.
+			{
+			const TUint32* longValue = reinterpret_cast<const TUint32*>(aValueOffset);
+			Mem::Copy(iDirEntryData+KValueOffsetFieldPosition, longValue, sizeof(TUint32));
+			if(totalSize == KThreeBytes)
+				{
+				Mem::FillZ(iDirEntryData+KValueOffsetFieldPosition+KThreeBytes, sizeof(TUint8));
+				}
+			}
+			break;
+		}
+	}
+
+
+// Sets the tag field (two bytes) of this Ifd entry.
+void CIfdGeneralEntry::SetTag(const TUint aTag)
+	{
+	Mem::Copy(iDirEntryData, &aTag, KSizeOfTagField);
+	}
+
+// Sets the format field (two bytes) of this Ifd entry.
+void CIfdGeneralEntry::SetFormat(const TUint aFormat)
+	{
+	Mem::Copy(iDirEntryData+KFormatFieldPosition, &aFormat, KSizeOfFormatField);
+	}
+
+// Sets the component count field (four bytes) of this Ifd entry.
+void CIfdGeneralEntry::SetComponentCount(const TUint aComponentCount)
+	{
+	Mem::Copy(iDirEntryData+KComponentCountFieldPosition, &aComponentCount, KSizeOfComponentCountField);
+	}
+	
+// Returns the tag value of this Ifd entry.
+TUint CIfdGeneralEntry::Tag()
+	{
+	return PtrReadUtil::ReadUint16(iDirEntryData);
+	}
+
+// Returns the format of this Ifd entry.
+TUint CIfdGeneralEntry::Format()
+	{
+	return PtrReadUtil::ReadUint16(iDirEntryData+KFormatFieldPosition);
+	}
+	
+// Returns the component count of this Ifd entry.
+TUint CIfdGeneralEntry::ComponentCount()
+	{
+	return PtrReadUtil::ReadUint32(iDirEntryData+KComponentCountFieldPosition);
+	}
+
+// Returns the value/offset field of this Ifd entry.
+TUint CIfdGeneralEntry::ValueOffset()
+	{
+	return PtrReadUtil::ReadUint32(iDirEntryData+KValueOffsetFieldPosition);
+	}
+
+// Returns a pointer to the data that the value/offset points to.
+const TUint8* CIfdGeneralEntry::ActualValue()
+	{
+	// If there is data that the value/offset field points to, then return a pointer to it.
+	return iActualData;
+	}
+
+TInt CIfdGeneralEntry::ExtraSize()
+	{
+	return TotalSize() - KMinimumIfdEntrySize;
+	}
+
+CIfdGeneralEntry* CIfdGeneralEntry::NewLC()
+	{
+	CIfdGeneralEntry* self = new (ELeave) CIfdGeneralEntry;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+	
+
+CIfdGeneralEntry* CIfdGeneralEntry::NewLC(const TUint& aTag, const TUint& aFormat, const TUint& aComponentCount, const TUint8* aParam, CIfdGeneral* aIfd)
+	{
+	TBool ignored; // NewLC is called by functions that don't require checking if the tag is known or not
+	CIfdGeneralEntry* self = new (ELeave) CIfdGeneralEntry();
+	CleanupStack::PushL(self);
+	self->ConstructL(aTag, aFormat, aComponentCount, aParam, aIfd, ignored);
+	return self;
+	}
+	
+CIfdGeneralEntry* CIfdGeneralEntry::NewL(const TUint& aTag, const TUint& aFormat, const TUint& aComponentCount, const TUint8* aParam, CIfdGeneral* aIfd, TBool& aUnknownTag)
+	{
+	CIfdGeneralEntry* self = new (ELeave) CIfdGeneralEntry();
+	CleanupStack::PushL(self);
+	self->ConstructL(aTag, aFormat, aComponentCount, aParam, aIfd, aUnknownTag);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CIfdGeneralEntry::CIfdGeneralEntry()
+	{
+	}
+
+void CIfdGeneralEntry::ConstructL()
+	{
+	iDirEntryData = static_cast<TUint8*>(User::Alloc(KMinimumIfdEntrySize));
+	if(iDirEntryData == NULL)
+		{
+		User::Leave(KErrNoMemory);
+		}
+	}
+
+void CIfdGeneralEntry::ConstructL(const TUint& aTag, const TUint& aFormat, TUint aComponentCount, const TUint8* aValueOffset, CIfdGeneral* aIfd, TBool& aUnknownTag)
+	{
+	// the code here used to check if the tag was invalid and if so leave with KErrArgument
+	// now if the tag is invalid it just bypasses the further checks
+	// this is to future proof against versions of exif which introduce additional tags
+	// to the exif 2.2 spec
+	TUint expectedComponentCount = 0;
+
+	ASSERT(aIfd);
+		
+	iIfd = aIfd;
+	if(aIfd->FindTag(aTag))
+		{
+		// Known tag
+		aUnknownTag = EFalse;
+		
+		TUint expectedFormat = 0;
+		
+		User::LeaveIfError(iIfd->GetFormat(aTag, expectedFormat));
+		User::LeaveIfError(iIfd->GetComponentCount(aTag, expectedComponentCount));
+
+		if ((aTag == KTagA002[ETagValue])||
+			(aTag == KTagA003[ETagValue])||
+			(aTag == KTag1001[ETagValue]) || 
+			(aTag == KTag1002[ETagValue]))
+			{
+			//do nothing Tag A002 A003 can be short or long 
+			// Interop Tag 1001 and 1002 can be unsigned short or long
+			}
+		else if(expectedFormat != aFormat)
+			{
+			User::Leave(KErrArgument);
+			}
+	
+		if(expectedComponentCount != 0) // If 0, then size is undefined.
+			{
+			if(aComponentCount > expectedComponentCount)
+				{
+				User::Leave(KErrNotSupported);
+				}
+			}
+		
+		// Check overflow.
+		if(aFormat >= KDataFormatSizeLength)
+			{
+			User::Leave(KErrOverflow);
+			}
+		}
+	else
+		{
+		// Unknown tag
+		// Create this entry anyway as it may be an extension to the EXIF standard
+		// but inform the caller
+		aUnknownTag = ETrue;
+		}
+	iDirEntryData = static_cast<TUint8*>(User::AllocL(KMinimumIfdEntrySize)); // Alloc standard entry size.
+	Mem::FillZ(iDirEntryData, KMinimumIfdEntrySize);
+	SetTag(aTag); // Simply set the tag.
+	SetFormat(aFormat); // Set the format (associated with the tag).
+	SetComponentCount(aComponentCount);	
+		
+	// Determine if we have a value or an offset to a value.
+	TInt bytes = KDataFormatSize[aFormat];
+	TUint size = aComponentCount * bytes;
+	TUint8* tempValue = NULL;
+	if(IsStringFormat())
+		{
+		tempValue = static_cast<TUint8*>(User::AllocL(size+sizeof(TUint8)));
+		Mem::Copy(tempValue, aValueOffset, size);
+		
+		if (tempValue[size-1] != KNullCharacter)
+			{	
+			Mem::Copy(tempValue+size, &KNullCharacter, sizeof(KNullCharacter));	
+			size++;
+			aComponentCount++;
+			SetComponentCount(aComponentCount);	
+			}
+			
+		aValueOffset = tempValue;
+		}				
+	else if(IsRationalFormat())
+		{
+		//Since rational default values only store the numerator in the CExifTag constant we
+		//must set the denominator.
+		if(expectedComponentCount == 1)
+			{
+			tempValue = static_cast<TUint8*>(User::AllocL(KSizeOfRational));
+			Mem::Copy(tempValue, aValueOffset, sizeof(TUint));
+			TUint denominator = 1;
+			Mem::Copy(tempValue+sizeof(TUint), &denominator, sizeof(TUint));
+			aValueOffset = tempValue;		
+			}
+		}
+	CleanupStack::PushL(TCleanupItem(Cleanup,tempValue));
+
+	if(size > KSizeOfValueOffsetField)
+		{
+		// We have an Offset, so will set the actual value (i.e. that which the value/offset field points to).
+		User::LeaveIfError(SetActualData(aValueOffset, aComponentCount, bytes));
+		// Will not set the value/offset field now, as will be set later once the whole block of data is needed.
+		}
+	else
+		{
+		// We have an actual value, so will set this in the ValueOffset.		
+		SetValueOffset(aValueOffset);
+		// The actual value field will be left blank - no need to store the data twice.
+		}	
+	CleanupStack::Pop(tempValue);
+	User::Free(tempValue);	
+	}
+	
+CIfdGeneralEntry::~CIfdGeneralEntry()
+	{
+	// from encoder
+	delete iDirEntryData;
+	delete iActualData;
+	}
+
+// Sets the data that the value/offset field points to.
+TInt CIfdGeneralEntry::SetActualData(const TAny* aActualData, const TUint aSize, const TUint aBytes)
+	{
+	/* 
+	Pass aSize, since we don't know how many components the value has.
+	Will need to check the format type, in order to overcome the problems with 'undefined' types.
+	*/
+	TInt size = aSize * aBytes;
+	iActualData = static_cast<TUint8*>(User::ReAlloc(iActualData, size));
+
+	if(iActualData == NULL)
+		{
+		return KErrNoMemory;
+		}
+	Mem::Copy(iActualData, aActualData, size);
+
+	SetComponentCount(aSize);
+	return KErrNone;
+	}
+
+// Updates a rational that has a component count of 1.	
+TInt CIfdGeneralEntry::UpdateRational(TInt aNumerator, TInt aDenominator)
+	{
+	// Cannot change a rational entry with a component count > 1 or a non-rational entry.
+	if(ComponentCount() > 1 || !IsRationalFormat())
+		{		
+		return KErrArgument;
+		}
+	// We're only dealing with a single rational value.
+	
+	// A rational is always > 4 bytes long, so we store it in the actual data field.	
+	TUint8* rational = static_cast<TUint8*>(User::Alloc(KSizeOfRational));
+	if(rational == NULL)
+		{
+		return KErrNoMemory;
+		}
+	TUint8* tempPtr = Mem::Copy(rational, &aNumerator, sizeof(aNumerator));
+    Mem::Copy(tempPtr, &aDenominator, sizeof(aDenominator));    
+	if(SetActualData(rational, sizeof(TUint8), KSizeOfRational) == KErrNoMemory)
+		{
+		User::Free(rational);
+		return KErrNoMemory;
+		}
+	User::Free(rational);
+	return KErrNone;
+	}
+
+// Updates a value of ASCII type when Ifd knowledge is needed.
+TInt CIfdGeneralEntry::UpdateString(const HBufC8* aStringValue)
+	{
+	if(aStringValue == NULL)
+		{
+		return KErrArgument;
+		}
+		
+	//Need to check format type of object.
+	if(!IsStringFormat())
+		{
+		// Cannot change non-ASCII entry.
+		return KErrArgument;
+		}
+			
+	TUint legalCompCount = KAnySize;
+	TInt err = KErrNone;
+
+	err = iIfd->GetComponentCount(Tag(), legalCompCount);
+	
+	TUint stringLength=aStringValue->Length();	
+	const TUint8* ptr = aStringValue->Ptr();		
+	TBool nullTerminationNeeded = EFalse;
+	
+	//Do some string length checks.
+	if (legalCompCount != KAnySize)
+		{
+		// We have a fixed length string type. Only accept strings that equal the
+		// fixed length when NULL termianted.
+		if (stringLength == legalCompCount-1 && ptr[stringLength-1] != KNullCharacter)
+			{
+			//If we add a NULL termination string will be legal
+			nullTerminationNeeded = ETrue;
+			}
+		else if (stringLength == legalCompCount && ptr[stringLength-1] == KNullCharacter)
+			{
+			//Okay as it is.
+			}
+		else
+			{
+			return KErrArgument;
+			}
+		}
+	else
+		{
+		//No length checks needed, but still might have to add a null terminator.
+		nullTerminationNeeded = (ptr[stringLength-1] != KNullCharacter);
+		}
+		
+	HBufC8* stringValueCopy = NULL;
+	if (nullTerminationNeeded)
+		{
+		stringValueCopy = HBufC8::New(aStringValue->Length()+1);
+		if(stringValueCopy == NULL)
+			{
+			return KErrNoMemory;
+			}
+
+		*stringValueCopy = *aStringValue;
+		stringValueCopy->Des().Append(KNullCharacter);
+		stringLength++;
+		
+		ptr = stringValueCopy->Ptr();			
+		}
+		
+	if(stringLength > KSizeOfValueOffsetField)
+		{
+		// We have an Offset, so will set the actual value.		
+		err = SetActualData(ptr, stringLength, sizeof(TUint8));
+		// Will NOT set the value/offset field now, since it will no-doubt change.
+		}
+	else
+		{
+		// We have an actual value, so will set this in the ValueOffset.		
+		SetComponentCount(stringLength);
+		SetValueOffset(ptr);
+		// The actualvalue field will be left blank - no need to store the data twice.
+		err=KErrNone;
+		}	
+	delete stringValueCopy;	
+	return err;
+	}
+	
+/*
+	Must have a catch-all update function since there are some anomolies to the standard rules.		
+	Furthermore, this function may also be used to set data of the undefined format.
+*/
+TInt CIfdGeneralEntry::UpdateData(TUint aComponentCount, const TUint8* aData)
+	{
+	TUint maxSize = 0;
+	// Get the maximum possible size.
+	TUint tag = Tag();
+		
+	iIfd->GetComponentCount(tag, maxSize);
+		
+	if(maxSize != KAnySize) // If KAnySize, then size is undefined.
+		{
+		if(aComponentCount > maxSize)
+			{
+			return KErrNotSupported;
+			}
+		}
+
+	// Check overflow.  Will indicate that the format-type is not recognised.
+	TUint format = Format();
+	if(format == 0)
+		{
+		return KErrCorrupt;
+		}
+	
+	if(format >= KDataFormatSizeLength)
+		{
+		return KErrNotSupported;
+		}
+		
+	// We are updating existing data, and so will already know the format type.
+	// Determine if we have a value or an offset to a value.
+	TInt bytes = KDataFormatSize[format];
+	TUint size = aComponentCount * bytes;
+
+	TUint8* tempValue = NULL;
+	// Add NULL to the end if it's ASCII
+	if(IsStringFormat())
+		{
+		// verify if the last character is NULL
+		if(aData[aComponentCount-1]==KNullCharacter)
+			{
+			//String already NULL terminated.
+			if ((maxSize != KAnySize) && (aComponentCount != maxSize))
+				{
+				return KErrNotSupported;
+				}
+			}
+		else
+			{
+			// Note: This branch is only executed when reading an Exif source
+			// that contains a non-NULL terminated ASCII string, which is 
+			// against the Exif 2.2 Spec.
+
+			// Since we will add a terminating NULL to the string,
+			// must check max size of this particular tag.
+			if((maxSize != KAnySize) && (aComponentCount != maxSize-1))
+				{
+				return KErrNotSupported;
+				}
+			
+			//Add the NULL terminator
+			tempValue = static_cast<TUint8*>(User::Alloc(size + sizeof(TUint8)));
+			if(tempValue == NULL)
+				{
+				return KErrNoMemory;
+				}
+			Mem::Copy(tempValue, aData, size);
+			Mem::Copy(tempValue+size, &KNullCharacter, sizeof(TUint8));
+			aData = tempValue;
+			size++;
+			aComponentCount++;
+			}
+		}
+	TInt err=KErrNone;
+	if(size > KSizeOfValueOffsetField)
+		{
+		// We have an Offset, so will set the actual value (i.e. that which the value/offset field points to).
+		err = SetActualData(aData, aComponentCount, bytes);
+		// Will not set the value/offset field now, as will be set later once the whole block of data is needed.
+		}
+	else
+		{
+		// We have an actual value, so will set this in the ValueOffset.		
+		SetComponentCount(aComponentCount);
+		SetValueOffset(aData);
+		// The actual value field will be left blank - no need to store the data twice.
+		}
+	User::Free(tempValue);
+	return err;
+	}
+	
+
+// Updates an integer value (including short, integer, long).
+TInt CIfdGeneralEntry::UpdateInteger(const TInt aIntegerValue)
+	{
+	// Need to check format type of object, to avoid calling this on an entry that is not an integer.
+	if(!IsIntegerFormat())
+		{
+		//first check if tag is 0xA002 or A003 - this could be short
+		//but when setting it we can set it as long
+		TUint16 tag = Tag();
+		if ((tag == KTagA002[ETagValue])||(tag == KTagA003[ETagValue]))
+			{
+			//tag is A002 or A003 and is short - set entry to long
+			SetFormat(EUnsignedLong);
+			}
+		else
+			{
+			// Cannot change non-integer entry.
+			return KErrArgument;
+			}
+		}
+	
+	// Check overflow.
+	TUint format = Format();
+	if(format == NULL)
+		{
+		return KErrCorrupt;
+		}
+	
+	if(format >= KDataFormatSizeLength)
+		{
+		return KErrNotSupported;
+		}
+	
+	// Determine if we have a value or an offset to a value.
+	TInt bytes = KDataFormatSize[format];
+	TUint size = ComponentCount() * bytes;
+	if(size > KSizeOfValueOffsetField)
+		{
+		// We have an Offset, so will set the actual value...
+		return SetActualData(&aIntegerValue, ComponentCount(), bytes);
+		// Will NOT set the value/offset field now, since it will no-doubt change.
+		}
+	else
+		{
+		// We have an actual value, so will set this in the ValueOffset.		
+		SetValueOffset(reinterpret_cast<const TUint8*>(&aIntegerValue));
+		return KErrNone;
+		// The actualvalue field will be left blank - no need to store the data twice.
+		}	
+	}
+	
+// Updates a short value.
+TInt CIfdGeneralEntry::UpdateShort(const TUint16 aShortValue)
+	{
+	// Need to check format type of object, to avoid calling this on an entry that is not a short.
+	if(!IsShortFormat())
+		{
+		// Cannot change non-short entry.
+		return KErrArgument;
+		}
+	
+	// Check overflow.
+	TUint format = Format();
+	if(format == NULL)
+		{
+		return KErrCorrupt;
+		}
+	
+	if(format >= KDataFormatSizeLength)
+		{
+		return KErrNotSupported;
+		}
+	
+	// Determine if we have a value or an offset to a value.
+	TInt bytes = KDataFormatSize[format]; // value is 2, since it is a short.
+	TUint size = ComponentCount() * bytes;
+	if(size > KSizeOfValueOffsetField)
+		{
+		// We have an Offset, so will set the actual value...
+		return SetActualData(&aShortValue, ComponentCount(), bytes);
+		// Will NOT set the value/offset field now, since it will no-doubt change.
+		}
+	else
+		{
+		// We have an actual value, so will set this in the ValueOffset.		
+		SetValueOffset(reinterpret_cast<const TUint8*>(&aShortValue));
+		return KErrNone;
+		// The actualvalue field will be left blank - no need to store the data twice.
+		}	
+	}
+
+// Returns the total size of this Ifd entry. The function should never return zero. 
+// The minimum value returned should be KMinimumIfdEntrySize, in case of any invalidity with
+// the entry such as invalid format.
+TInt CIfdGeneralEntry::TotalSize()
+	{
+	TUint16 format = Format();
+	TBool formatValidity = EFalse;
+	if(format <= KDataFormatSizeLength-1)
+		{
+		//formats - 0, 6, 8, are invalid, which will have size 0.
+		if(KDataFormatSize[format] != 0)
+			{
+			formatValidity = ETrue;
+			}
+		}
+	ASSERT(formatValidity);
+	//in case of invalid format, return minimum size
+	if (!formatValidity) 
+		{
+		return KMinimumIfdEntrySize;
+		}
+		
+	TUint size = (KDataFormatSize[format] * ComponentCount());
+	if(size > KSizeOfValueOffsetField)
+		{
+		// We have an offset value, so include extra data length (i.e. past the KMinimumIfdEntrySize byte limit).
+		if ((size%2)==1) 
+			{
+			size++;
+			}
+		return KMinimumIfdEntrySize + size;
+		}
+	return KMinimumIfdEntrySize;
+	}
+	
+// Does this entry contain rational data?
+TBool CIfdGeneralEntry::IsRationalFormat()
+	{
+	TUint format = Format();
+	return format == EUnsignedRational || 
+			format == ESignedRational;
+	}
+	
+// Does this entry contain string data?
+TBool CIfdGeneralEntry::IsStringFormat()
+	{
+	return Format() == EAscii;
+	}
+	
+// Does this entry contain 'undefined' data?
+TBool CIfdGeneralEntry::IsUndefinedFormat()
+	{
+	return Format() == EUndefined;
+	}
+
+// Does this entry contain integer data?
+TBool CIfdGeneralEntry::IsIntegerFormat()
+	{	
+	switch(Format())
+		{
+		case EByte:		
+		case EUnsignedLong:
+		case ESignedLong:
+			{
+			return ETrue;
+			}			
+		default:
+			{
+			return EFalse;	
+			}
+		}		
+	}
+	
+// Does this entry contain short data?
+TBool CIfdGeneralEntry::IsShortFormat()
+	{	
+	return Format() == EUnsignedShort;	
+	}
+	
+// Returns ETrue if the data is Unicode.
+TBool CIfdGeneralEntry::IsUnicodeData()
+	{
+	TUint tag = Tag();
+	//Only the following undefined tags may potentially be Unicode
+	TBool tagIsPotentiallyUnicode=(	KTag9286[ETagValue] == tag		|| 
+									KTagGPS001B[ETagValue] == tag 	||
+									KTagGPS001C[ETagValue] == tag		);
+								
+	// Need to ensure that the correct component count is returned for the unicode data.
+	if (tagIsPotentiallyUnicode && ExtraSize() >= KUnicode().Size())
+		{
+		return (0 == Mem::Compare(iActualData, KUnicode().Length() , KUnicode().Ptr(), KUnicode().Length()));
+		}
+	else
+		{
+		return EFalse;
+		}
+	}