imaging/imagingplugins/codecs/JPEGCodec/Exif/ExifThumbnailGenerator.cpp
changeset 0 5752a19fdefe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imaging/imagingplugins/codecs/JPEGCodec/Exif/ExifThumbnailGenerator.cpp	Wed Aug 25 12:29:52 2010 +0300
@@ -0,0 +1,500 @@
+// 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 "ExifThumbnailGenerator.h"
+#include <imageconversion.h>
+#include <bitmaptransforms.h>
+#include "JpegTypes.h"
+#include "jpegwritecodec.h"
+
+#include "ExifGeneralConsts.h"
+
+#include "exiftransformdataaccessor.h"
+
+CScaledJpegGenerator* CScaledJpegGenerator ::NewL(TRequestStatus* aNotifier, const MExifSource* aSource, 
+	TBool aMaintainAspectRatio, CImageDecoder::TOptions aDecoderOptions)
+	{
+	CScaledJpegGenerator* self= new (ELeave) CScaledJpegGenerator(aNotifier, aMaintainAspectRatio, aDecoderOptions);
+	CleanupStack::PushL(self);
+	self->ConstructL(aSource);
+	CleanupStack::Pop(self);
+	
+	return self;	
+	}
+	
+HBufC8* CScaledJpegGenerator::GetJpegDataL()
+	{
+	if(iState!=EFinished)
+		{
+		User::Leave(KErrInUse);
+		}
+	if(iJPegImage.Count()==0)
+		{
+		User::Leave(KErrNotFound);
+		}
+
+	TInt size=0;
+	TInt i;
+	for(i=0; i<iJPegImage.Count(); i++)
+		{
+		size += iJPegImage[i]->Length();
+		}
+	HBufC8* thumbnailData=HBufC8::NewL(size);
+	TPtr8 bufPtr(thumbnailData->Des());
+	
+	for(i=0; i<iJPegImage.Count(); i++)
+		{
+		bufPtr.Append(*(iJPegImage[i]));
+		}
+	
+	return thumbnailData;
+	}
+
+HBufC8* CScaledJpegGenerator::JpegDataBufferL(const TUint aIndex)
+	{
+	if (iState != EFinished)
+		{
+		User::Leave(KErrInUse);
+		}
+	if (iJPegImage.Count() == 0)
+		{
+		User::Leave(KErrNotFound);
+		}
+
+	if (aIndex < iJPegImage.Count())
+		{
+		return iJPegImage[aIndex]->AllocL();
+		}
+	else
+		{
+		return NULL;
+		}
+	}
+	
+CScaledJpegGenerator::CScaledJpegGenerator(TRequestStatus* aNotifier, TBool aMaintainAspectRatio, 
+	CImageDecoder::TOptions aDecoderOptions)
+	: CActive(EPriorityStandard), iNotifier(aNotifier), iState(EFinished), 
+	iMaintainAspectRatio(aMaintainAspectRatio)
+	{	
+	iDecoderOptions = aDecoderOptions;
+	}
+
+CScaledJpegGenerator::~CScaledJpegGenerator()
+	{
+	Cancel();
+	CleanData();
+	iJPegImage.Close();
+	}
+	
+void CScaledJpegGenerator::ConstructL(const MExifSource* aSource)
+	{
+	ASSERT(aSource != NULL);
+	iSource = aSource;
+	CActiveScheduler::Add(this);
+	}
+	
+
+
+void CScaledJpegGenerator::StartL(TSize aSize, TImageToGenerate aImageType)
+	{
+	if(iState!=EFinished)
+		{
+		User::Leave(KErrInUse);
+		}
+	
+	
+	CleanResultData();
+	CleanTempData();
+	iSize=aSize;
+	iImageType=aImageType;
+	
+	if(!iImageDecoder)
+		{
+		iImageDecoder = iSource->CreateImageDecoderL(iDecoderOptions);
+		TFrameInfo frameInfo= iImageDecoder->FrameInfo();
+
+		if(iBitmap)
+			{
+			delete iBitmap;
+			iBitmap = NULL;
+			}
+		iBitmap= new (ELeave) CFbsBitmap();
+		
+		User::LeaveIfError(iBitmap->Create(frameInfo.iFrameCoordsInPixels.Size(), frameInfo.iFrameDisplayMode));
+		iState=EDecode;
+		iImageDecoder->Convert(&iStatus, *iBitmap);
+		SetActive();
+		}
+	else
+		{	// the initialisation has already been done, there is no need to do it again
+		DoScaleBitmapL();
+		SetActive();
+		}
+	*iNotifier=KRequestPending;
+	}
+
+void CScaledJpegGenerator::RunL()
+	{
+	if(iStatus.Int()==KErrNone)
+		{
+		switch(iState)
+			{
+			case EDecode:
+				// the decode is finished, we now scale the image
+				DoScaleBitmapL();
+				SetActive();
+				break;
+				
+			case EScale:
+				 // the scaling is over. We now Convert the image
+				DoConvertL();
+				SetActive();
+				break;
+			case EConvert:
+				{
+				switch(iFrameState)
+					{
+					case EFrameIncomplete:
+						DoConvertL();
+						SetActive();
+						break;
+					case EFrameComplete:
+						GenerationCompleteL();
+						break;
+					default:
+						iStatus=KErrCorrupt;
+						RunError(KErrCorrupt);
+					}
+				}
+				break;
+			case EFinished:
+				break;
+			default:
+				break;
+			}
+		// we have the bitmap, now scale it
+		}
+	else
+		{
+		RunError(iStatus.Int());
+		}
+	}
+	
+void CScaledJpegGenerator::DoCancel()
+	{
+	if (iImageDecoder)
+		{
+		iImageDecoder->Cancel();
+		}
+	if (iBitmapScaler)
+		{
+		iBitmapScaler->Cancel();
+		}
+	CleanData();
+	User::RequestComplete(iNotifier, KErrCancel);
+	iState=EFinished;
+	}
+	
+void CScaledJpegGenerator::DoScaleBitmapL()
+	{
+	if(iBitmapScaler)
+		{
+		delete iBitmapScaler;
+		iBitmapScaler = NULL;
+		}
+	iBitmapScaler=CBitmapScaler::NewL();
+
+	iBitmapScaler->UseLowMemoryAlgorithm(ETrue);
+	iBitmapScaler->Scale(&iStatus, *iBitmap, iSize, iMaintainAspectRatio);
+	iState=EScale;
+	}
+
+
+ // returns ETrue if it is the last chunk of data to be received
+void CScaledJpegGenerator::DoConvertL()
+	{
+	if(iState!=EConvert)
+		{
+		delete iBitmapScaler;
+		iBitmapScaler=NULL;
+		CreateJpegCodecL();
+		if(iJpegChunck)
+			{
+			delete iJpegChunck;
+			iJpegChunck = NULL;
+			}
+		iJpegChunck=HBufC8::NewMaxL(4096);
+		iJpegChunckPtr.Set(*iJpegChunck);
+
+		if(iImageType==EThumbnail)
+			{
+			// write SOI
+			iJpegChunckPtr[0] = (TUint8)((KJpgSOISignature & 0xff00) >> 8);
+			iJpegChunckPtr[1] = (TUint8)(KJpgSOISignature & 0xff);
+			iJpegChunckPtr.Shift(2);
+			}
+		
+		iJpegCodec->InitFrameL(iJpegChunckPtr, *iBitmap);
+		
+		if(iImageType==EThumbnail)
+			{
+			// Adjust iJpegChunckPtr to account for SOI added above 
+			TInt len=iJpegChunckPtr.Length() + 2;
+			iJpegChunckPtr.Set(*iJpegChunck);
+			iJpegChunckPtr.SetLength(len);		
+			}
+			
+		HBufC8* buf = iJpegChunckPtr.AllocLC();
+		iJPegImage.AppendL(buf);
+		CleanupStack::Pop(buf);
+		iJpegChunckPtr.Set(*iJpegChunck);
+		iFrameState=EFrameIncomplete;
+		}
+	else
+		{
+		iFrameState= iJpegCodec->ProcessFrameL(iJpegChunckPtr);
+		HBufC8* buf = iJpegChunckPtr.AllocLC();
+		iJPegImage.AppendL(buf);
+		CleanupStack::Pop(buf);
+		}	
+	
+
+	iStatus=KRequestPending;
+	TRequestStatus* status=&iStatus;
+	User::RequestComplete(status, KErrNone);
+	iState=EConvert;
+	}
+
+
+void CScaledJpegGenerator::CreateJpegCodecL()
+	{
+	const CFrameImageData* frameImageData= &iImageDecoder->FrameData();
+
+	//PDEF115929
+	//This should not be the case - corrupt file
+	if (frameImageData == NULL)
+		{
+		User::Leave(KErrCorrupt);
+		}
+
+	TJpgFrameInfo frameInfo;
+	TInt qualityFactor;
+
+	qualityFactor = 0;
+
+ 	TInt count = frameImageData->ImageDataCount();
+
+	//PDEF115929
+	//This should not be the case - corrupt file
+	if (count == 0)
+		{
+		User::Leave(KErrCorrupt);
+		}
+
+	TBool jpgImageDataProcessed = EFalse;
+	for (TInt index = 0 ; index<count ; index++)
+		{	
+		const TImageDataBlock& encoderData = *frameImageData->GetImageData(index);
+		if (encoderData.DataType() == KJPGImageDataUid)
+			{
+			// Leave if we have already processed one of these.
+			if (jpgImageDataProcessed)
+				User::Leave(KErrCorrupt);
+
+			const TJpegImageData& jpegImageData = STATIC_CAST(const TJpegImageData&, encoderData);
+			qualityFactor = jpegImageData.iQualityFactor;
+
+			TJpegImageData::TColorSampling sampleScheme = jpegImageData.iSampleScheme;
+			frameInfo.iComponent[0].iQTable = 0;
+			if (sampleScheme == TJpegImageData::EMonochrome)
+				{
+				frameInfo.iNumberOfComponents = 1;
+				frameInfo.iComponent[0].iHorzSampleFactor = 1;
+				frameInfo.iComponent[0].iVertSampleFactor = 1;
+				}
+			else
+				{
+				frameInfo.iNumberOfComponents = 3;
+				switch (sampleScheme)
+					{
+				case TJpegImageData::EColor420:
+					frameInfo.iComponent[0].iHorzSampleFactor = 2;
+					frameInfo.iComponent[0].iVertSampleFactor = 2;
+					frameInfo.iComponent[1].iVertSampleFactor = 1;
+					frameInfo.iComponent[2].iVertSampleFactor = 1;
+					break;
+				case TJpegImageData::EColor422:
+					frameInfo.iComponent[0].iHorzSampleFactor = 2;
+					frameInfo.iComponent[0].iVertSampleFactor = 1;
+					frameInfo.iComponent[1].iVertSampleFactor = 1;
+					frameInfo.iComponent[2].iVertSampleFactor = 1;
+					break;
+				case TJpegImageData::EColor444:
+					frameInfo.iComponent[0].iHorzSampleFactor = 1;
+					frameInfo.iComponent[0].iVertSampleFactor = 1;
+					frameInfo.iComponent[1].iVertSampleFactor = 1;
+					frameInfo.iComponent[2].iVertSampleFactor = 1;
+					break;
+				default:
+					User::Leave(KErrNotSupported);
+					}
+
+				frameInfo.iComponent[1].iHorzSampleFactor = 1;
+				frameInfo.iComponent[1].iQTable = 1;
+				frameInfo.iComponent[2].iHorzSampleFactor = 1;
+				frameInfo.iComponent[2].iQTable = 1;
+				}
+			
+			frameInfo.iMaxHorzSampleFactor = frameInfo.iComponent[0].iHorzSampleFactor;
+			frameInfo.iMaxVertSampleFactor = frameInfo.iComponent[0].iVertSampleFactor;
+
+			jpgImageDataProcessed = ETrue;
+			}
+		else if (encoderData.DataType() == KJPGQTableUid)
+			{
+			const TJpegQTable& jpegQTable = STATIC_CAST(const TJpegQTable&, encoderData);
+			TInt tableIndex = jpegQTable.iTableIndex;
+			if ((tableIndex != TJpegQTable::ELumaTable) && (tableIndex != TJpegQTable::EChromaTable))
+				{
+				// This can happen if client has given us a second chroma QTable
+				// replacement.  We do not support this at present, so we ignore
+				// the extra Q table and encode using the same QTable for both
+				// chroma components.
+				continue;
+				}
+
+			TUint8 values[KJpgQTableEntries];
+			TUint8* valuePtr = values;
+			const TUint8* zigZagPtr = KZigZagSequence.iZigZag;
+			const TUint8* valuePtrLimit = valuePtr + KJpgQTableEntries;
+
+			while (valuePtr < valuePtrLimit)
+				*valuePtr++ = jpegQTable.iEntries[*zigZagPtr++];
+
+			if (tableIndex == TJpegQTable::ELumaTable)
+				{
+				delete iLumaTable;
+				iLumaTable=NULL;
+				iLumaTable = new(ELeave) TQTable;
+
+				iLumaTable->Set(values, EFalse);
+				}
+			else // tableIndex must be TJpegQTable::EChromaTable
+				{
+				delete iChromaTable;
+				iChromaTable=NULL;
+				iChromaTable = new(ELeave) TQTable;
+
+				iChromaTable->Set(values, EFalse);
+				}
+			}
+		else if ((encoderData.DataType() == KJPGCommentUid))
+			{
+			if (iImageType == EMainImage) 
+				{
+				const TJpegComment& jpegComment = STATIC_CAST(const TJpegComment&, encoderData);
+				if (!jpegComment.iComment)
+					User::Leave(KErrNotFound);
+
+				if ((jpegComment.iComment->Length() == 0) || (jpegComment.iComment->Length() > 65534))
+					User::Leave(KErrNotSupported);
+	            
+	        	HBufC8* thisComment = jpegComment.iComment->AllocL();
+		    	TInt ret = iComment.Append(thisComment);
+			    if (ret != KErrNone)
+			    	{
+			    	delete thisComment;
+			    	User::Leave(ret);
+				    }
+				
+				}
+			}
+		else
+			User::Leave(KErrCorrupt);
+		}
+
+	iJpegCodec= CJpgWriteCodec::NewL(frameInfo,qualityFactor,iLumaTable,iChromaTable,iComment,NULL);
+	}
+
+
+TInt CScaledJpegGenerator::RunError(TInt aError)
+	{
+	CleanData();
+	User::RequestComplete(iNotifier, aError);
+	iState=EFinished;
+	
+	return KErrNone;
+	}
+	
+void CScaledJpegGenerator::GenerationCompleteL()
+	{
+	// write EOI
+	TBuf8<2> buffer2Bytes;
+	buffer2Bytes.SetLength(2);
+	TUint8* headerPtr2 = &buffer2Bytes[0];
+	headerPtr2[0] = (TUint8)((KJpgEOISignature & 0xff00) >> 8);
+	headerPtr2[1] = (TUint8)(KJpgEOISignature & 0xff);
+	HBufC8* buf = buffer2Bytes.AllocLC();
+	iJPegImage.AppendL(buf);
+	CleanupStack::Pop(buf);
+	iState=EFinished;
+	CleanTempData();
+	User::RequestComplete(iNotifier, KErrNone);
+	}
+
+void CScaledJpegGenerator::CleanTempData()
+	{
+	delete iJpegCodec;
+	iJpegCodec=NULL;
+	delete iBitmapScaler;
+	iBitmapScaler=NULL;
+
+	delete iJpegChunck;
+	iJpegChunck=NULL;
+
+	iComment.ResetAndDestroy();
+	delete iLumaTable;
+	iLumaTable=NULL;
+	delete iChromaTable;
+	iChromaTable=NULL;
+	}
+
+void CScaledJpegGenerator::CleanResultData()
+	{
+	iJPegImage.ResetAndDestroy();
+	}
+	
+void CScaledJpegGenerator::CleanData()
+	{
+	CleanTempData();
+	CleanResultData();
+	
+	delete iImageDecoder;
+	iImageDecoder=NULL;
+	delete iBitmap;
+	iBitmap=NULL;
+	}
+
+void CScaledJpegGenerator::GetScaledImageSize(TSize &aSize)
+	{
+	if (iBitmap)
+		{
+		aSize = iBitmap->SizeInPixels();
+		}
+	else
+		{
+		aSize = iSize;
+		}
+	}