mmplugins/imagingplugins/codecs/JPEGCodec/Exif/ExifEditUtility.cpp
changeset 0 40261b775718
equal deleted inserted replaced
-1:000000000000 0:40261b775718
       
     1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "exiftransform.h"
       
    17 #include "exiftransformdataaccessor.h"
       
    18 #include "ExifEditUtility.h"
       
    19 #include "ExifThumbnailGenerator.h"
       
    20 #include "ExifGeneralConsts.h"
       
    21 #include "JpegConsts.h"
       
    22 #include "ImageUtils.h"
       
    23 
       
    24 #include <f32file.h>
       
    25 
       
    26 const TUint8 KJpegMarkerByte = 0xff;
       
    27 const TUint8 KDhtMarkerByte = 0xc4;
       
    28 const TUint8 KDqtMarkerByte = 0xdb;
       
    29 const TUint8 KSosMarkerByte = 0xda;
       
    30 
       
    31 
       
    32 const TUint KTableTypeSize = 1;		// in DHT and DQT the table type is one byte long
       
    33 const TUint KEntriesInQTable = 64;	// number of entries in a specific quantization table
       
    34 
       
    35 const TUint KDCTableSize = 28;		// see Exif 2.2 specification
       
    36 const TUint KACTableSize = 178;		// see Exif 2.2 specification
       
    37 
       
    38 const TUint KDQTTypicalSize = (KTableTypeSize + KEntriesInQTable) * 3;
       
    39 const TUint KDHTTypicalSize = (KTableTypeSize + KDCTableSize) * 2 + (KTableTypeSize + KACTableSize) * 2;
       
    40 
       
    41 const TInt KJpegBlockTypeUndefined = -1;
       
    42 
       
    43 _LIT(KDhtBlockHeader, "\xff\xc4\x00\x00");	// jpeg DHT block id followed by placeholder for block size
       
    44 _LIT(KDqtBlockHeader, "\xff\xdb\x00\x00");	// jpeg DQT block id followed by placeholder for block size
       
    45 	
       
    46 CExifEditUtility* CExifEditUtility::NewL(MExifSource* aSource, MExifDest* aDest, TBool aIgnoreExifMetadataProcessing)
       
    47 	{
       
    48 	CExifEditUtility * self= new (ELeave) CExifEditUtility(aIgnoreExifMetadataProcessing);
       
    49 	CleanupStack::PushL(self);
       
    50 	self->ConstructL(aSource, aDest);
       
    51 	CleanupStack::Pop(self);
       
    52 	
       
    53 	return self;
       
    54 	}
       
    55 	
       
    56 CExifEditUtility::~CExifEditUtility()
       
    57 	{
       
    58 	Cancel();
       
    59 	delete iScaledJpegGenerator;
       
    60 	delete iExifData;
       
    61 	iDHTTables.Close();
       
    62 	iDQTTables.Close();	
       
    63 	iTempCopyBuffer.Close();
       
    64 	}
       
    65 	
       
    66 CExifEditUtility::CExifEditUtility(TBool aIgnoreExifMetadataProcessing)
       
    67 	: CActive(EPriorityStandard)
       
    68 	, iState(EEmpty)
       
    69 	, iIgnoreExifMetadataProcessing(aIgnoreExifMetadataProcessing)
       
    70 	{
       
    71 	iLookupSlackBuff.SetLength( 0 );
       
    72 	}
       
    73 	
       
    74 void CExifEditUtility::ConstructL(MExifSource* aSource, MExifDest* aDest)
       
    75 	{
       
    76 	ASSERT(aSource != NULL);
       
    77 	ASSERT(aDest != NULL);
       
    78 	iSource = aSource;
       
    79 	iDest = aDest;
       
    80 	CActiveScheduler::Add(this);
       
    81 	}
       
    82 	
       
    83 
       
    84 MExifMetadata* CExifEditUtility::ExifMetadata()
       
    85 	{
       
    86 	return iExifData;
       
    87 	}
       
    88 
       
    89 
       
    90 //
       
    91 //
       
    92 //
       
    93 //
       
    94 //		interface methods
       
    95 //	
       
    96 
       
    97 void CExifEditUtility::ReadSourceL()
       
    98 	{
       
    99 	if(iState!=EEmpty)
       
   100 		{
       
   101 		User::Leave(KErrInUse);
       
   102 		}
       
   103 	
       
   104 	InitReadL();
       
   105 	if(!iIgnoreExifMetadataProcessing)
       
   106 		{
       
   107 		delete iExifData;
       
   108 		iExifData = NULL;
       
   109 		TRAPD(err, iExifData = iSource->ReadAndConvertExifDataL(iHeaderBlockSize + 
       
   110 			(sizeof(KJpgSOISignature)+sizeof(KJpgMarker)) /*size of the app1 marker*/, 
       
   111 			iExifBlockSize));
       
   112 		if(err != KErrNone)
       
   113 			{
       
   114 			if(err == KErrNoMemory)
       
   115 				{
       
   116 				User::Leave(KErrNoMemory);					
       
   117 				}
       
   118 			iIgnoreExifMetadataProcessing = ETrue;
       
   119 			iExifBlockSize = 0;
       
   120 			iConvertToJfif = ETrue;				
       
   121 			}
       
   122 
       
   123 		}
       
   124 	iState=EReadComplete;
       
   125 	}
       
   126 	
       
   127 
       
   128 void CExifEditUtility::WriteDestL(TBool aEncodeThumbnail, const TSize& aSize, TBool aPreserveImage, TBool aMaintainAspectRatio, TRequestStatus* aNotifier)
       
   129 	{
       
   130 	iNotifier=aNotifier;
       
   131 	iEncodeThumbnail=aEncodeThumbnail;
       
   132 	iSize= aSize;
       
   133 	iPreserveImage= aPreserveImage;
       
   134 	iMaintainAspectRatio = aMaintainAspectRatio;
       
   135 	if(iState !=EReadComplete)
       
   136 		{
       
   137 		RunError(KErrInUse);
       
   138 		return;
       
   139 		}
       
   140 	
       
   141 	TInt err=iDest->Init();
       
   142 	if(err!=KErrNone)
       
   143 		{
       
   144 		RunError(err);
       
   145 		return;
       
   146 		}
       
   147 
       
   148 	iState=EStartWrite;
       
   149 	ProcessCommandL();
       
   150 	*iNotifier=KRequestPending;
       
   151 	}
       
   152 
       
   153 
       
   154 //
       
   155 //		interface methods
       
   156 //	
       
   157 //
       
   158 //
       
   159 //
       
   160 
       
   161 
       
   162 
       
   163 
       
   164 
       
   165 
       
   166 //
       
   167 //
       
   168 //
       
   169 //
       
   170 //		methods from CActive
       
   171 //
       
   172 void CExifEditUtility::DoCancel()
       
   173 	{
       
   174 	iState=ECancelled;
       
   175 		
       
   176 	if(iScaledJpegGenerator)
       
   177 		{
       
   178 		iScaledJpegGenerator->Cancel();
       
   179 		}
       
   180 		
       
   181 	//Now reset everything to how it was before the transform.
       
   182 	iState = EReadComplete;	
       
   183 	iSource->Cancel();
       
   184 	iDest->Cancel();
       
   185 			
       
   186 	//We need to cancel both the internal state machine request status
       
   187 	//and the client request status	
       
   188 	if (iStatus == KRequestPending)
       
   189 		{
       
   190 		TRequestStatus* status=&iStatus;
       
   191 		User::RequestComplete(status, KErrCancel);
       
   192 		}
       
   193 	if (iNotifier)//but we do need to check iNotifier TRequestStatus
       
   194 		{
       
   195 		if (*iNotifier == KRequestPending)
       
   196 			{
       
   197 			User::RequestComplete(iNotifier,KErrCancel);
       
   198 			}
       
   199 		}
       
   200 	}
       
   201 
       
   202 
       
   203 void CExifEditUtility::RunL()
       
   204 	{
       
   205 	if(iStatus.Int()==KErrNone)
       
   206 		{
       
   207 		ProcessCommandL();
       
   208 		}
       
   209 	else
       
   210 		{
       
   211 		RunError(iStatus.Int());
       
   212 		}
       
   213 	}
       
   214 
       
   215 TInt CExifEditUtility::RunError(TInt aError)
       
   216 	{					
       
   217 	if(iState<EReadComplete)
       
   218 		{
       
   219 		delete iExifData;
       
   220 		iExifData = NULL;
       
   221 		iState=EEmpty;
       
   222 		iSource->CleanupAfterEarlyError();
       
   223 		iDest->CleanupAfterEarlyError();
       
   224 		}
       
   225 	else 
       
   226 		{
       
   227 		iState=EReadComplete;
       
   228 		iSource->CleanupAfterLateError();
       
   229 		iDest->CleanupAfterLateError();
       
   230 		}
       
   231 		
       
   232 	User::RequestComplete(iNotifier, aError);
       
   233 	return KErrNone;
       
   234 	}
       
   235 //
       
   236 //		interface methods
       
   237 //	
       
   238 //
       
   239 //
       
   240 //
       
   241 
       
   242 
       
   243 void CExifEditUtility::ProcessCommandL()
       
   244 	{
       
   245 	TBool doContinue=EFalse;
       
   246 	switch(iState)
       
   247 		{
       
   248 		// writer commands
       
   249 		case EStartWrite:
       
   250 			doContinue=DoCopyBlockL(0, iHeaderBlockSize, EWriteWriteHeader);
       
   251 			iState=ECopying;
       
   252 			break;
       
   253 		case EWriteWriteHeader:
       
   254 			if(!iIgnoreExifMetadataProcessing)
       
   255 				{
       
   256 				doContinue=DoWriteReadThumbnailL();
       
   257 				}
       
   258 			else
       
   259 				{
       
   260 				//skip to next step.
       
   261 				iStatus=KRequestPending;
       
   262 				TRequestStatus* reqStat=&iStatus;
       
   263 				User::RequestComplete(reqStat, KErrNone);
       
   264 				doContinue = ETrue; 
       
   265 				}
       
   266 			iState = EReadThumbnail;
       
   267 			break;
       
   268 		case EReadThumbnail:
       
   269 			doContinue=DoReadThumbnailL();		
       
   270 			if (iPreserveImage)
       
   271 				{
       
   272 				iState=EWriteReadThumbnail;
       
   273 				}
       
   274 			else
       
   275 				{
       
   276 				iState=EWriteWriteExif;
       
   277 				}
       
   278 			break;		
       
   279 		case EWriteReadThumbnail:
       
   280 			doContinue=DoWriteConvertExifL();
       
   281 			iState=EWriteConvertExif;
       
   282 			break;
       
   283 		case EWriteConvertExif:
       
   284 			if(iIgnoreExifMetadataProcessing && (iExifBlockSize >0))
       
   285 				{
       
   286 				HBufC8* destBuffer = iIOBufferPtr.AllocL();
       
   287 				iDest->SetDestBuffer(destBuffer); // Give iDest ownership of buffer
       
   288 				}
       
   289 			doContinue=DoWriteDestBufferL();
       
   290 			
       
   291 			if(iPreserveImage)
       
   292 				{
       
   293 				iState=EWriteWriteExif;
       
   294 				}
       
   295 			else
       
   296 				{
       
   297 				iState=EWriteReadMainImage;
       
   298 				}
       
   299 			break;
       
   300 		case EWriteWriteExif:
       
   301 			if(iPreserveImage)
       
   302 				{
       
   303 				doContinue=DoCopyBlockL(iTrailerOffset, iTrailerBlockSize, EWriteTrailer, iDestIsExif);
       
   304 				iState=ECopying;
       
   305 				}
       
   306 			else
       
   307 				{
       
   308 				doContinue=DoCreateMainImageL();
       
   309 				iCurrentWriteBufferIndex = 0;
       
   310 				iState=EWriteReadThumbnail;
       
   311 				}
       
   312 			break;
       
   313 		case EWriteTrailer:
       
   314 			DoWriteComplete();
       
   315 			iState=EReadComplete;
       
   316 			break;
       
   317 		case EWriteReadMainImage:
       
   318 			doContinue=DoWriteScaledImageL();
       
   319 			break;
       
   320 		case ECancelled:
       
   321 			return;
       
   322 		case ECopying:
       
   323 			iState=DoCopyNextBlockL();
       
   324 			doContinue=ETrue;
       
   325 			break;
       
   326 		default:
       
   327 			break;
       
   328 		}
       
   329 		
       
   330 	if(doContinue)
       
   331 		{
       
   332 		SetActive();
       
   333 		}
       
   334 	}
       
   335 
       
   336 
       
   337 
       
   338 
       
   339 //
       
   340 //
       
   341 //
       
   342 //
       
   343 //		Readers and writers
       
   344 //	each performs one step of the process
       
   345 
       
   346 
       
   347 //
       
   348 //			Readers
       
   349 //
       
   350 void CExifEditUtility::InitReadL()
       
   351 	{
       
   352 	iSource->InitL();
       
   353 	
       
   354 	TBool exifFound=EFalse;
       
   355 	
       
   356 	TUint16 jpegMarker=0;
       
   357 	TUint position=0;
       
   358 	TUint16 blockSize=0;
       
   359 	// we want to preserve the image data.
       
   360 	// so we parse the source file, and isolate the exif metadata
       
   361 	for(;;)
       
   362 		{		
       
   363 		position+=blockSize;
       
   364 		jpegMarker=KJpgSOISignature;
       
   365 		User::LeaveIfError(iSource->SetReadPosition(position));
       
   366 		do
       
   367 			{
       
   368 			jpegMarker=iSource->ReadUint16L();	
       
   369 			position+=sizeof(TUint16);
       
   370 			}while(jpegMarker==KJpgSOISignature);
       
   371 
       
   372 		if(jpegMarker==KJpgApp0Signature || jpegMarker==KJpgApp1Signature)
       
   373 			{
       
   374 			blockSize=iSource->ReadUint16L();
       
   375 			position+=sizeof(TUint16);
       
   376 			if (jpegMarker==KJpgApp1Signature)
       
   377 				{
       
   378 				exifFound=ETrue;
       
   379 				break;					
       
   380 				}
       
   381 			}
       
   382 		else
       
   383 			{
       
   384 			break;
       
   385 			}
       
   386 		}
       
   387 
       
   388 	User::LeaveIfError(iSource->Size(iTrailerBlockSize));
       
   389 	iTrailerOffset=sizeof(KJpgSOISignature);
       
   390 	if(exifFound)
       
   391 		{
       
   392 		// Set our reading parameters
       
   393 		TUint sizeOfApp1Marker = sizeof(KJpgSOISignature)+sizeof(KJpgMarker);
       
   394 		if(position <= sizeOfApp1Marker) User::Leave(KErrCorrupt);//source file is corrupted.
       
   395 		iHeaderBlockSize=position - sizeOfApp1Marker;	
       
   396 		iExifBlockSize=blockSize;
       
   397 		iTrailerOffset+=iHeaderBlockSize+blockSize;
       
   398 		}
       
   399 	else
       
   400 		{
       
   401 		iHeaderBlockSize=sizeof(KJpgSOISignature); 		
       
   402 		iExifBlockSize=0;
       
   403 		}
       
   404 	iTrailerBlockSize-= iTrailerOffset;
       
   405 	}
       
   406 
       
   407 //			Readers
       
   408 //
       
   409 //
       
   410 
       
   411 
       
   412 //
       
   413 //			Writers
       
   414 //
       
   415 TBool CExifEditUtility::DoWriteConvertExifL()
       
   416 	{
       
   417 	if(iIgnoreExifMetadataProcessing)
       
   418 		{
       
   419 		if(iExifBlockSize)
       
   420 			{
       
   421 			//copy the EXIF metadata en-block, without parsing it first.
       
   422 			iSource->ReadL(iHeaderBlockSize, iIOBufferPtr, iExifBlockSize + KBlockSizeLength, iStatus);
       
   423 			iDestIsExif = ETrue;
       
   424 			}
       
   425 		else
       
   426 			{ // JUMP to next step... no EXIF metadata information, so nothing to ignore.
       
   427 			if (iConvertToJfif)
       
   428 				{
       
   429 				//Create a buffer to hold a default JFIF header
       
   430 				HBufC8* buffer=HBufC8::NewMaxL(KJfifApp0DataSize+sizeof(KJpgApp0Signature));
       
   431 				TPtr8 buf(buffer->Des());
       
   432 				TJpegUtilities::CreateJfifHeader(buf);
       
   433 				iDest->SetDestBuffer(buffer);// Give iDest ownership of buffer				
       
   434 				}
       
   435 
       
   436 			iStatus=KRequestPending;	
       
   437 			TRequestStatus* reqStat=&iStatus;
       
   438 			User::RequestComplete(reqStat, KErrNone);
       
   439 			iDestIsExif = EFalse;
       
   440 			}
       
   441 		}
       
   442 	else
       
   443 		{	
       
   444 		TInt err = KErrNone;
       
   445 		if(iExifBlockSize > 0 || iExifData->IsExifDataModified())
       
   446 			{
       
   447 			HBufC8* buffer = NULL;
       
   448 			
       
   449 			UpdateImageSizeTagsL();
       
   450 			iExifData->CheckUpdateMandatoryTagsL();
       
   451 			
       
   452 			err= iExifData->CreateExifChunk(buffer); // buffer not owned by iExifData
       
   453 			if (err == KErrNone)
       
   454 				{
       
   455 				iDest->SetDestBuffer(buffer); // Give iDest ownership of buffer
       
   456 				}	
       
   457 			iExifData->ResetExifDataModified();
       
   458 			iDestIsExif = ETrue;	
       
   459 			}
       
   460 		else
       
   461 			{
       
   462 			iDestIsExif = EFalse;
       
   463 			}
       
   464 		iStatus=KRequestPending;	
       
   465 		TRequestStatus* reqStat=&iStatus;
       
   466 		User::RequestComplete(reqStat, err);	
       
   467 		}
       
   468 	return ETrue;	//we continue to the next step
       
   469 	}
       
   470 
       
   471 TBool CExifEditUtility::DoWriteReadThumbnailL()
       
   472 	{
       
   473 	if(iEncodeThumbnail)
       
   474 		{
       
   475 		HBufC8* buffer = iExifData->GetJpegThumbnailData(); // Does not transfer ownership of buffer
       
   476 		if(buffer==NULL)
       
   477 			{
       
   478 			delete iScaledJpegGenerator;
       
   479 			iScaledJpegGenerator=NULL;
       
   480 			// we have to generate the thumbnail
       
   481 			iScaledJpegGenerator=CScaledJpegGenerator::NewL(&iStatus, iSource);
       
   482 			iScaledJpegGenerator->StartL(TSize(KThumbnailWidth, KThumbnailHeight), CScaledJpegGenerator::EThumbnail);
       
   483 			}
       
   484 		else
       
   485 			{
       
   486 			iStatus=KRequestPending;
       
   487 			TRequestStatus* reqStat=&iStatus;
       
   488 			User::RequestComplete(reqStat, KErrNone);
       
   489 			}
       
   490 		}
       
   491 	else
       
   492 		{
       
   493 		iExifData->SetThumbnailData(NULL);
       
   494 		iStatus=KRequestPending;
       
   495 		TRequestStatus* reqStat=&iStatus;
       
   496 		User::RequestComplete(reqStat, KErrNone);
       
   497 		}
       
   498 
       
   499 	return ETrue;
       
   500 	}
       
   501 
       
   502 TBool CExifEditUtility::DoReadThumbnailL()
       
   503 	{
       
   504 	
       
   505 	if(iEncodeThumbnail && iScaledJpegGenerator)
       
   506 		{
       
   507 		HBufC8* thumb=iScaledJpegGenerator->GetJpegDataL(); // thumb not owned by iScaledJpegGenerator
       
   508 		delete iScaledJpegGenerator;
       
   509 		iScaledJpegGenerator=NULL;
       
   510 		iExifData->SetThumbnailData(thumb); // Give iExifData ownership of thumb
       
   511 		}
       
   512 	iStatus=KRequestPending;	
       
   513 	TRequestStatus* reqStat=&iStatus;
       
   514 	User::RequestComplete(reqStat, KErrNone);
       
   515 	return ETrue;
       
   516 	}
       
   517 
       
   518 TBool CExifEditUtility::DoCreateMainImageL()
       
   519 	{
       
   520 	if(!iScaledJpegGenerator)
       
   521 		{
       
   522 		iScaledJpegGenerator=CScaledJpegGenerator::NewL(&iStatus, iSource, iMaintainAspectRatio, 
       
   523 			CImageDecoder::EOptionIgnoreExifMetaData);
       
   524 		}
       
   525 	iScaledJpegGenerator->StartL(iSize, CScaledJpegGenerator::EMainImage);
       
   526 	return ETrue;
       
   527 	}
       
   528 
       
   529 TBool CExifEditUtility::DoWriteScaledImageL()
       
   530 	{
       
   531 	HBufC8* buffer = iScaledJpegGenerator->JpegDataBufferL(iCurrentWriteBufferIndex);
       
   532 	if (buffer)
       
   533 		{
       
   534 		iCurrentWriteBufferIndex++;
       
   535 		iState = EWriteReadMainImage;
       
   536 		}
       
   537 	else
       
   538 		{
       
   539 		iState = EWriteTrailer;
       
   540 		}
       
   541 	iDest->SetDestBuffer(buffer); // Give iDest ownership of buffer
       
   542 	return DoWriteDestBufferL();
       
   543 	}
       
   544 
       
   545 void CExifEditUtility::SetUpTablesL()
       
   546 	{
       
   547 	// set up the descriptors that store the combined tables
       
   548 
       
   549 	iDHTTables.Zero();
       
   550 	if (iDHTTables.MaxLength() < KDHTTypicalSize)
       
   551 		{
       
   552 		iDHTTables.ReAllocL(KDHTTypicalSize);
       
   553 		}
       
   554 	iDHTTables.Append(KDhtBlockHeader);	// write the DHT block id and placeholder size
       
   555 	
       
   556 	iDQTTables.Zero();
       
   557 	if (iDQTTables.MaxLength() < KDQTTypicalSize)
       
   558 		{
       
   559 		iDQTTables.ReAllocL(KDQTTypicalSize);
       
   560 		}
       
   561 	iDQTTables.Append(KDqtBlockHeader);	// write the DQT block id and placeholder size
       
   562 	}
       
   563 
       
   564 void CExifEditUtility::AppendTableDataL(RBuf8& aTables, const TDesC8& aData)
       
   565 	{
       
   566 	if (aTables.Length() + aData.Length() > aTables.MaxLength())
       
   567 		{
       
   568 		aTables.ReAllocL(aTables.Length() + aData.Length());
       
   569 		}
       
   570 	aTables.Append(aData);
       
   571 	}
       
   572 	
       
   573 TBool CExifEditUtility::DoCopyBlockL(TUint aStart, TInt aLength, TConvertState aNextStep, TBool aCombineTables)
       
   574 	{
       
   575 	iCurrentJpegBlockType = KJpegBlockTypeUndefined;
       
   576 	iCurrentJpegBlockSize = 0;
       
   577 
       
   578 	TUint bytesToRead = 0;
       
   579 	if (aCombineTables)
       
   580 		{
       
   581 		// EXIF only supports one DHT table and one DQT table (JFIF supports multiple tables)
       
   582 		// When copying the data, keep track of jpeg blocks and store any DHT and DQT tables
       
   583 		// Write the combined tables out before the SOS block
       
   584 
       
   585 		SetUpTablesL();
       
   586 
       
   587 		iCurCopyState = EReadJpegBlockInfo;
       
   588 		bytesToRead = KBlockIdAndSizeLength;
       
   589 		}
       
   590 	else
       
   591 		{
       
   592 		// copy the data in multiples of KIOBlockSize
       
   593 		iCurCopyState = ERead;				
       
   594 		bytesToRead = Min(aLength, KIOBlockSize);
       
   595 		}
       
   596 
       
   597 	iSource->ReadL(aStart, iIOBufferPtr, bytesToRead, iStatus);
       
   598 	iNextStep = aNextStep;
       
   599 	iCopyLength = aLength - bytesToRead;
       
   600 	return ETrue;	//we continue to the next step
       
   601 	}
       
   602 
       
   603 void CExifEditUtility::DoReadL()
       
   604 	{
       
   605 	TUint bytesToRead = 0;
       
   606 	if (iCurrentJpegBlockType == KJpegBlockTypeUndefined)
       
   607 		{
       
   608 		// start/continue reading data without tracking jpeg block info
       
   609 		iCurCopyState = ERead;
       
   610 		bytesToRead = Min(iCopyLength, KIOBlockSize);
       
   611 		}
       
   612 	else if (iCurrentJpegBlockSize == 0)
       
   613 		{
       
   614 		// read the next jpeg block's info
       
   615 		iCurCopyState = EReadJpegBlockInfo;
       
   616 		bytesToRead = KBlockIdAndSizeLength - iLookupSlackBuff.Length();
       
   617 		}
       
   618 	else
       
   619 		{
       
   620 		// start/continue reading data from this jpeg block
       
   621 		iCurCopyState = ERead;
       
   622 		bytesToRead = Min(iCurrentJpegBlockSize, KIOBlockSize);
       
   623 		iCurrentJpegBlockSize -= bytesToRead;	// keep track of how much block data is left to read 
       
   624 		}
       
   625 	iSource->NextReadL(iIOBufferPtr, bytesToRead, iStatus);
       
   626 	iCopyLength -= bytesToRead;	
       
   627 	}
       
   628 
       
   629 void CExifEditUtility::HandleJpegBlockL(const TDesC8& aPrependedData)
       
   630 	{
       
   631 	switch (iCurrentJpegBlockType)
       
   632 		{
       
   633 	case KDhtMarkerByte:
       
   634 	case KDqtMarkerByte:
       
   635 		DoReadL();
       
   636 		break;
       
   637 	case KSosMarkerByte:
       
   638 		{
       
   639 		// reached the SOS block so write out the tables
       
   640 		
       
   641 		// calculate the size of the combined DHT table data
       
   642 		TInt totalSize = iDHTTables.Length() - KBlockIdLength;
       
   643 		
       
   644 		// fill in the size placeholder
       
   645 		iDHTTables[2] = totalSize >> 8;
       
   646 		iDHTTables[3] = totalSize & 0xff;
       
   647 		
       
   648 		AppendTableDataL(iDHTTables, iIOBufferPtr);
       
   649 
       
   650 		// calculate the size of the combined DQT table data
       
   651 		totalSize = iDQTTables.Length() - KBlockIdLength;
       
   652 
       
   653 		// fill in the size placeholder
       
   654 		iDQTTables[2] = totalSize >> 8;
       
   655 		iDQTTables[3] = totalSize & 0xff;
       
   656 		
       
   657 		AppendTableDataL(iDQTTables, iDHTTables);
       
   658 
       
   659 		iDest->WriteL(iDQTTables, iDQTTables.Length(), iStatus);
       
   660 		break;
       
   661 		}
       
   662     case (KJpgCommentSignature & 0xFF):
       
   663 	case (KJpgApp0Signature & 0xFF):
       
   664         if (iDestIsExif)
       
   665             {
       
   666             // According to the EXIF 2.2 spec. we can't have comments
       
   667             // and shouldn't have APP0, so skip them.
       
   668 			// APP0 can be skipped entirely using the block length.
       
   669 			// In case of comment block, crawl slowly, as the comment blocks may be corrupted
       
   670 			if(iCurrentJpegBlockType == (KJpgApp0Signature & 0xFF))
       
   671             	{
       
   672 	            TInt fileSize = 0;
       
   673 				User::LeaveIfError(iSource->Size(fileSize));
       
   674 				ASSERT(fileSize > 0);
       
   675 	            TUint positionAfterCurBlock = fileSize - iCopyLength + iCurrentJpegBlockSize;
       
   676 	            if(positionAfterCurBlock > fileSize)
       
   677 	            	{
       
   678 	            	User::Leave(KErrCorrupt);
       
   679 	            	}
       
   680 				User::LeaveIfError(iSource->SetReadPosition(positionAfterCurBlock));            	
       
   681             	}
       
   682             iCurrentJpegBlockType = 0;
       
   683             iCurrentJpegBlockSize = 0;
       
   684             DoReadL();
       
   685             break;	
       
   686             }	
       
   687             // if we'are not in EXIF mode fall to default block handling
       
   688 	default:
       
   689 	    {
       
   690  	    TInt len = aPrependedData.Length() +  iIOBufferPtr.Length();
       
   691  	    if (iTempCopyBuffer.MaxLength() < len)
       
   692  	    	{
       
   693  	    	iTempCopyBuffer.ReAllocL(len);
       
   694  	    	}
       
   695  	    iTempCopyBuffer = aPrependedData;
       
   696         iTempCopyBuffer.Append( iIOBufferPtr );
       
   697         iDest->WriteL(iTempCopyBuffer, len, iStatus);
       
   698 		break;
       
   699 		}
       
   700 	    } // switch
       
   701 	}
       
   702 
       
   703 TConvertState CExifEditUtility::DoCopyNextBlockL()
       
   704 	{
       
   705 	TConvertState result = ECopying;
       
   706 	
       
   707 	switch (iCurCopyState)
       
   708 		{
       
   709 	case EReadJpegBlockInfo:
       
   710 		{
       
   711 		iCurCopyState = EWrite;
       
   712 
       
   713 		// iIOBuffer should contain in order:
       
   714 		// - the jpeg marker byte (0xff)
       
   715 		// - the block id byte
       
   716 		// - the high byte of the block size
       
   717 		// - the low byte of the block size
       
   718 		const TText8* dataPtr = iIOBufferPtr.Ptr();
       
   719 		const TText8* const dataPtrLimit = dataPtr + iIOBufferPtr.Length();
       
   720 		if (iLookupSlackBuff.Length()==0 ) 
       
   721 		    {
       
   722     		while (dataPtr < dataPtrLimit && *dataPtr != KJpegMarkerByte)
       
   723     		    {
       
   724     		    dataPtr++;
       
   725     		    }
       
   726     		const TInt dataLeft = TUint(dataPtrLimit - dataPtr);
       
   727     		ASSERT(dataLeft >= 0 );
       
   728     		if (dataLeft < KBlockIdAndSizeLength) 
       
   729     		    {
       
   730     		    if (dataLeft > 0) // some partial marker, take a copy of that data
       
   731     		        {
       
   732     		        iLookupSlackBuff.Copy(dataPtr, dataLeft);
       
   733     		        }
       
   734     		    DoReadL();   
       
   735     		    break;
       
   736     		    }
       
   737 
       
   738     		// we should have some marker and block length in the buffer
       
   739 		    iCurrentJpegBlockType = dataPtr[1];
       
   740 		    iCurrentJpegBlockSize = (dataPtr[2] << 8) + dataPtr[3] - KBlockSizeLength;    		    
       
   741 		    }
       
   742 		else    // we have some marker in a slack buffer, so combine it with current data
       
   743 		    {
       
   744 		    if (iLookupSlackBuff.Length() + iIOBufferPtr.Length() < KBlockIdAndSizeLength)
       
   745 		        {
       
   746 		        User::Leave(KErrCorrupt); // unexpected and of image data
       
   747 		        }
       
   748 		    TInt needToAppend = KBlockIdAndSizeLength - iLookupSlackBuff.Length();
       
   749 		    iLookupSlackBuff.Append( iIOBufferPtr.Left( needToAppend ) );
       
   750 		    iIOBufferPtr.Shift( needToAppend );
       
   751 		    iCurrentJpegBlockType = iLookupSlackBuff[1];
       
   752 		    iCurrentJpegBlockSize = (iLookupSlackBuff[2] << 8) + iLookupSlackBuff[3] - KBlockSizeLength;    		    		    
       
   753 		    }
       
   754 		//check validity of the block size
       
   755 		if(iCurrentJpegBlockSize > iCopyLength)        
       
   756 			{        
       
   757 			User::Leave(KErrCorrupt);        
       
   758 			}        
       
   759 
       
   760 		HandleJpegBlockL(iLookupSlackBuff);
       
   761         iLookupSlackBuff.SetLength( 0 );
       
   762 		break;
       
   763 		}
       
   764 	case ERead:
       
   765 		{
       
   766 		iCurCopyState = EWrite;
       
   767 
       
   768 		switch (iCurrentJpegBlockType)
       
   769 			{
       
   770 		case KDhtMarkerByte:
       
   771 			// store the DHT block data
       
   772 			AppendTableDataL(iDHTTables, iIOBufferPtr);
       
   773 			DoReadL();
       
   774 			break;
       
   775 		case KDqtMarkerByte:
       
   776 			// store the DQT block data
       
   777 			AppendTableDataL(iDQTTables, iIOBufferPtr);
       
   778 			DoReadL();
       
   779 			break;
       
   780 		default:
       
   781 			iDest->WriteL(iIOBufferPtr, iIOBufferPtr.Length(), iStatus);
       
   782 			ASSERT(iCopyLength >= 0);
       
   783 			if (iCopyLength == 0)
       
   784 				{
       
   785 				result = iNextStep;
       
   786 				}
       
   787 			break;
       
   788 			}
       
   789 		
       
   790 		break;
       
   791 		}
       
   792 	case EWrite:
       
   793 		{
       
   794 		if (iCurrentJpegBlockType == KSosMarkerByte)
       
   795 			{
       
   796 			iCurrentJpegBlockType = KJpegBlockTypeUndefined;	// tables have been written out, no need to track the jpeg blocks now
       
   797 			}
       
   798 		DoReadL();
       
   799 		break;
       
   800 		}
       
   801 	default:
       
   802 		break;
       
   803 		}
       
   804 
       
   805 	return result;
       
   806 	}
       
   807 
       
   808 
       
   809 TBool CExifEditUtility::DoWriteComplete()
       
   810 	{
       
   811 	iDest->WriteComplete();
       
   812 	
       
   813 	User::RequestComplete(iNotifier, KErrNone);
       
   814 	return EFalse;	
       
   815 	}
       
   816 
       
   817 
       
   818 TBool CExifEditUtility::DoWriteDestBufferL()
       
   819 	{
       
   820 	iDest->WriteDestBufferL(iStatus);
       
   821 	return ETrue;
       
   822 	}
       
   823 //			Writers
       
   824 //
       
   825 //
       
   826 
       
   827 //
       
   828 //		Readers and writers
       
   829 //	each performs one step of the process
       
   830 //
       
   831 //
       
   832 //
       
   833 
       
   834 	
       
   835 void CExifEditUtility::UpdateImageSizeTagsL()
       
   836 	{
       
   837 	if (iPreserveImage)
       
   838 		{
       
   839 		if (!iExifData->CheckImageSizeTags())
       
   840 			{
       
   841 			TSize destSize;
       
   842 			// In this case, jpeg data blocks are copied from source to dest, 
       
   843 			// so we need CImageDecoder to get the size of the image.
       
   844 			// NOTE: normally image size would be copied from valid Exif source 
       
   845 			// but this is for the case where that data is missing from the source.
       
   846 			CImageDecoder* decoder = NULL;
       
   847 			TRAPD(err, decoder = iSource->CreateImageDecoderL(CImageDecoder::EOptionNone));
       
   848 			if (err != KErrNone)
       
   849 				{
       
   850 				destSize = iSize;
       
   851 				}
       
   852 			else
       
   853 				{
       
   854 				destSize = decoder->FrameInfo().iFrameCoordsInPixels.Size();
       
   855 				delete decoder;
       
   856 				}
       
   857 			iExifData->UpdateImageSizeTagsL(destSize);
       
   858 			}
       
   859 		}
       
   860 	else
       
   861 		{
       
   862 		TSize destSize;
       
   863 		if (iScaledJpegGenerator) 
       
   864 			{
       
   865 			iScaledJpegGenerator->GetScaledImageSize(destSize);
       
   866 			}
       
   867 		else
       
   868 			{
       
   869 			destSize = iSize;
       
   870 			}
       
   871 		iExifData->UpdateImageSizeTagsL(destSize);
       
   872 		}
       
   873 	}
       
   874 
       
   875