os/graphics/graphicsdeviceinterface/directgdiadaptation/hwsrc/directgdidriverprocessstate.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2007-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 "directgdidriverprocessstate.h"
    17 
    18 
    19 const TInt KMaxProcessStateHeapSize = 0x100000;
    20 
    21 
    22 /** 
    23 Reports any error that occurs. 
    24 This is #defined so that we retain correct line information for the debug stream.
    25 */
    26 #define ProcessEglError(err) \
    27 	{ \
    28 	const EGLint eglError = eglGetError(); \
    29 	if (eglError == EGL_BAD_ALLOC) \
    30 		err = KErrNoMemory; \
    31 	else if (eglError == EGL_CONTEXT_LOST) \
    32 		err = KErrDied; \
    33 	else \
    34 		err = KErrGeneral; \
    35 	TBuf16<256> _message; \
    36 	_message.Format(_L16("EGL Error: %x\n"), eglError); \
    37 	GRAPHICS_LOGD_DEBUG(_message); \
    38 	}
    39 /**
    40 Image source data constructor.
    41 @param aSgImageId The unique identifier of the RSgImage used to create the EGLImage and VGImage.
    42 @param aEglImage The EGLImage created from the RSgImage.
    43 @param aVgImage The VGImage created from aEglImage.
    44 @panic  DGDIAdapter 48, if invalid images are passed in.
    45 */
    46 CImageSourceData::CImageSourceData(const TSgDrawableId& aSgImageId, EGLImageKHR aEglImage, VGImage aVgImage) :
    47 	iSgImageId(aSgImageId),
    48 	iEglImage(aEglImage),
    49 	iVgImage(aVgImage)
    50 	{
    51 	GRAPHICS_ASSERT_DEBUG(iEglImage != EGL_NO_IMAGE_KHR, EDirectGdiPanicImageSourceDataConstructorError);
    52 	GRAPHICS_ASSERT_DEBUG(iVgImage != VG_INVALID_HANDLE, EDirectGdiPanicImageSourceDataConstructorError);
    53 	}
    54 
    55 /**
    56 Image source data destructor.
    57 @panic  DGDIAdapter 49, if reference count is not zero when this funtion is called.
    58 */
    59 CImageSourceData::~CImageSourceData()
    60 	{
    61 	GRAPHICS_ASSERT_ALWAYS(iRefCount == 0, EDirectGdiPanicImageSourceDataRefCountError);
    62 	}
    63 
    64 XDirectGdiDriverProcessState::XDirectGdiDriverProcessState()
    65 	{
    66 	iCreateMutexErr = iMutex.CreateLocal();
    67 	}
    68 
    69 
    70 /**
    71 Creates a heap to store EGL and VG images. This heap will be shared between threads in 
    72 the current process. This is necessary as EGL images are shared between threads, and an attempt
    73 to create an EGL image from a RSgImage in two different threads would fail if the EGL image was not 
    74 also shared. Also gets function pointers to image creation and destruction methods in EGL and saves 
    75 them for use later. Note that Initialize() must be called before you call CreateVgImage() or
    76 DestroyVgImage().
    77 
    78 @pre The client has called MutexCreationStatus() and checked for KErrNone. 
    79 
    80 @param aDisplay The current EGL display handle 
    81 
    82 @return KErrNone if successful, KErrNoMemory if we fail to create the heap, KErrGeneral
    83 if we encounter an EGL error, otherwise one of the system-wide error codes.
    84  */
    85 TInt XDirectGdiDriverProcessState::Initialize(EGLDisplay aDisplay)
    86 	{
    87 	iMutex.Wait();
    88 	
    89 	// No need to re-initialize everything if we have already been initialized.
    90 	TInt err = KErrNone;
    91 	if (iLocalHeap == NULL)
    92 		{
    93 		// Create a heap that will be used to store EGLImages and VGImages that are created
    94 		// when the driver creates a source image (in CDirectGdiDriverImpl::CreateDrawableSource())
    95 		iLocalHeap = UserHeap::ChunkHeap(NULL, 0, KMaxProcessStateHeapSize);
    96 		if (iLocalHeap == NULL)
    97 			{
    98 			err = KErrNoMemory;		
    99 			}
   100 		}
   101 	
   102 	// Check the availability of the required EGL/VG extensions
   103 	if (err == KErrNone)
   104 		{
   105 		if (!iCheckedExtensionsAvailable)
   106 			{
   107 			err = CheckAllEglExtensions(aDisplay);				
   108 			iCheckedExtensionsAvailable = ETrue;
   109 			
   110 #ifdef _DEBUG_DIRECTGDI
   111 			RDebug::Printf("VG_VENDOR: %s\n", vgGetString(VG_VENDOR));
   112 			RDebug::Printf("VG_RENDERER : %s\n", vgGetString(VG_RENDERER));
   113 			RDebug::Printf("VG_VERSION : %s\n", vgGetString(VG_VERSION));
   114 #endif	
   115 			}
   116 		}
   117 					
   118 	// Get function pointers for EGLImage creation and destruction and VGImage creation EGL functions.
   119 	// These need to be refreshed every time this process state is initialised as it cannot be guaranteed
   120 	// that the function pointers will remain the same after this process state was last closed
   121 	// (and EGL was terminated).
   122 	if (err == KErrNone)
   123 		{
   124 		iPfnEglCreateImageKHR = reinterpret_cast<TeglCreateImageKHRTypefPtr>(eglGetProcAddress("eglCreateImageKHR"));		
   125 		if (iPfnEglCreateImageKHR == NULL)
   126 			{
   127 			ProcessEglError(err);
   128 			err = KErrNotSupported;
   129 			}		
   130 		}
   131 	
   132 	if (err == KErrNone)
   133 		{
   134 		iPfnEglDestroyImageKHR = reinterpret_cast<TeglDestroyImageKHRTypefPtr>(eglGetProcAddress("eglDestroyImageKHR"));	
   135 		if (iPfnEglDestroyImageKHR == NULL)
   136 			{
   137 			ProcessEglError(err);
   138 			err = KErrNotSupported;
   139 			}		
   140 		}
   141 	
   142 	if (err == KErrNone)
   143 		{
   144 		iPfnVgCreateImageTargetKHR = reinterpret_cast<TvgCreateEGLImageTargetKHRTypefPtr>(eglGetProcAddress("vgCreateEGLImageTargetKHR"));		
   145 		if (iPfnVgCreateImageTargetKHR == NULL)
   146 			{
   147 			ProcessEglError(err);
   148 			err = KErrNotSupported;
   149 			}		
   150 		}
   151 	iMutex.Signal();
   152 		
   153 	return err;
   154 	}
   155 
   156 /**
   157 Destructor, asserts that the instance count is zero, and the count of images stored is zero.
   158 
   159 @panic DGDIAdapter 50, if the instance count is not zero (debug-only).
   160 @panic DGDIAdapter 51, if the image count is not zero (debug-only).
   161  */
   162 XDirectGdiDriverProcessState::~XDirectGdiDriverProcessState()
   163 	{
   164 	if (iLocalHeap != NULL)
   165 		{
   166 		iLocalHeap->Close();
   167 		}
   168 	iMutex.Close();
   169 		
   170 	GRAPHICS_ASSERT_DEBUG(iDriverInstanceCount == 0, EDirectGdiPanicProcessStateInstanceCountError);	
   171 	GRAPHICS_ASSERT_DEBUG(iImages.Count() == 0, EDirectGdiPanicProcessStateImageCountError);
   172 	}
   173 
   174 /**
   175 Creates a VGImage from the passed RSgImage. This method first creates a EGLImageKHR from
   176 the RSgImage, from which it can create a VGImage. The EGLImageKHR and the VGImage are saved
   177 so that they can be shared if a user attempts to create another VGImage from the same RSgImage.
   178 If a VGImage has already been created from the passed RSgImage, the existing image is returned
   179 instead of a new one being created. This will increment the reference count on that stored image.
   180 
   181 @param aDisplay The EGL display to associate the image with.
   182 @param aVgImageRet A handle to the VGImage created is returned here.
   183 @param aSgImage The VGImage will be created from this RSgImage.
   184 
   185 @pre Initialize() has been called.
   186 
   187 @return KErrNone if the image is created successfully, KErrGeneral if an EGL error occurs,
   188 otherwise one of the system-wide error codes.
   189 
   190 @panic DGDIAdapter 33, if one of the member EGL function pointers is NULL (debug-only).
   191 If this happens it may be that Initialize() has not been called for this process state.
   192 @panic DGDIAdapter 36, if aDisplay is EGL_NO_DISPLAY (debug-only).
   193  */
   194  TInt XDirectGdiDriverProcessState::CreateVgImage(EGLDisplay aDisplay, VGImage& aVgImageRet, const RSgImage& aSgImage)
   195 	{
   196 	VGImage vgImage = VG_INVALID_HANDLE;
   197 	TInt err = KErrNone;	
   198 	aVgImageRet = VG_INVALID_HANDLE;
   199 	
   200 	iMutex.Wait();	
   201 	// If this image has already been created, just return a handle to the
   202 	// previously created one.
   203 	if (ImageExists(aSgImage, vgImage))
   204 		{
   205 		aVgImageRet = vgImage;
   206 		iMutex.Signal();
   207 		return KErrNone;
   208 		}
   209 	
   210 	// If the function pointers are NULL, Initialize() may not have been called
   211 	GRAPHICS_ASSERT_DEBUG(iPfnEglCreateImageKHR, EDirectGdiPanicProcessStateNotInitialized);	
   212 	GRAPHICS_ASSERT_DEBUG(iPfnEglDestroyImageKHR, EDirectGdiPanicProcessStateNotInitialized);	
   213 	GRAPHICS_ASSERT_DEBUG(iPfnVgCreateImageTargetKHR, EDirectGdiPanicProcessStateNotInitialized);
   214 	GRAPHICS_ASSERT_DEBUG(aDisplay != EGL_NO_DISPLAY, EDirectGdiPanicNoDisplay);
   215 	
   216 	// Create an EGLImage from the passed RSgImage, then create a VGImage from that and 
   217 	// assign the VGImage to the newly created source. (The EGL image creation functions
   218 	// have already been retreived at this stage as function pointers using eglGetProcAddress()
   219 	// in InitialiseEgl())	
   220 	
   221 	// Pass in the preserved attribute. Without this the existing image data could be trashed.
   222 	EGLint createImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, 
   223 								   		 EGL_NONE};
   224 	
   225 	EGLImageKHR eglImage = iPfnEglCreateImageKHR(aDisplay, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, reinterpret_cast<EGLClientBuffer>(&aSgImage), createImageAttribs);
   226 	if (eglImage == EGL_NO_IMAGE_KHR)
   227 		{
   228 		ProcessEglError(err);
   229 		}
   230 		
   231 	if (err == KErrNone)
   232 		{
   233 		vgImage = iPfnVgCreateImageTargetKHR(eglImage);	
   234 		if (vgImage == VG_INVALID_HANDLE)
   235 			{				
   236 			ProcessEglError(err);
   237 			}		
   238 		}
   239 		
   240 	if (err == KErrNone)
   241 		{
   242 		// Add the image to the array of images on the process heap
   243 		err = AddImage(aSgImage, eglImage, vgImage);
   244 		}
   245 	
   246 	if (err == KErrNone)
   247 		{		
   248 		aVgImageRet = vgImage;
   249 		}
   250 	else
   251 		{		
   252 		// Clean up
   253 		if (vgImage != VG_INVALID_HANDLE)
   254 			{
   255 			vgDestroyImage(vgImage);
   256 			}
   257 		iPfnEglDestroyImageKHR(aDisplay, eglImage);		
   258 		}
   259 	
   260 	iMutex.Signal();
   261 	
   262 	return err;
   263 	}
   264  
   265 /**
   266 Destroys the passed VGImage. Searches for the image in the image array and
   267 decrements its reference count if found. Destroys the image when the reference count
   268 reaches zero.
   269 
   270 @param aDisplay The EGLDisplay the VGImage to be destroyed was created with.
   271 @param aVgImage A handle to the VGImage to destroy.
   272 
   273 @return KErrNone if the image is destroyed successfully, otherwise one of the system-wide
   274 error codes.
   275 
   276 @panic DGDIAdapter 33, if the EGL function pointer for destroying a VGImage is NULL (debug-only).
   277  */
   278 TInt XDirectGdiDriverProcessState::DestroyVgImage(EGLDisplay aDisplay, VGImage aVgImage)
   279 	{	
   280 	GRAPHICS_ASSERT_DEBUG(iPfnEglDestroyImageKHR, EDirectGdiPanicProcessStateNotInitialized);
   281 	
   282 	iMutex.Wait();
   283 	TInt err = RemoveImage(aDisplay, aVgImage);	
   284 	iMutex.Signal();
   285 	
   286 	return err;
   287 	}
   288 
   289 /**
   290 Increments the instance count.
   291 */
   292 void XDirectGdiDriverProcessState::OpenDriver()
   293 	{
   294 	iMutex.Wait();
   295 	++iDriverInstanceCount;
   296 	iMutex.Signal();
   297 	}
   298 
   299 /**
   300 Decrements the instance count.
   301 If this is the last instance of a DirectGDI driver, the associated EGL display is terminated, and
   302 there should be no images left in the array as they should all have been destroyed.
   303 
   304 @param aDriverEglDisplay An EGLDisplay associated with the driver being closed. If this is the last
   305        driver being closed, the display is destroyed.
   306 @panic DGDIAdapter 51, if the image count is not zero (debug-only).
   307  */
   308 void XDirectGdiDriverProcessState::CloseDriver(EGLDisplay& aDriverEglDisplay)
   309 	{
   310 	iMutex.Wait();
   311 	if (--iDriverInstanceCount == 0 && aDriverEglDisplay != EGL_NO_DISPLAY)
   312 		{
   313 		eglTerminate(aDriverEglDisplay);	
   314 		aDriverEglDisplay = EGL_NO_DISPLAY;
   315 		// Set the function pointers to NULL as cannot be guaranteed that they will be recreated at the same address
   316 		// when EGL is next initialised.
   317 		iPfnEglCreateImageKHR = NULL;
   318 		iPfnEglDestroyImageKHR = NULL;
   319 		iPfnVgCreateImageTargetKHR = NULL;
   320 		GRAPHICS_ASSERT_DEBUG(iImages.Count() == 0, EDirectGdiPanicProcessStateImageCountError);
   321 		}
   322 	iMutex.Signal();
   323 	}
   324 
   325 /** 
   326 Check that the following EGL extensions are available:
   327 	EGL_SYMBIAN_PIXMAP_TYPE_RSGIMAGE
   328 	EGL_KHR_image
   329 	EGL_SYMBIAN_image_preserved
   330 	
   331 According to the spec, OpenVG extensions cannot be checked until a context has been made current, 
   332 which has not happened at this point, so currently there is no check for VG_KHR_EGL_image extension. 
   333 	
   334 @pre eglInitialize() must have been called for the passed EGLDisplay.	
   335 	
   336 @param aDisplay The current EGL display handle. 	
   337 
   338 @return KErrNone if all the extensions are available, KErrNotSupported if one or more
   339 of the extensions are not supported by the EGL driver.	
   340  */
   341 TInt XDirectGdiDriverProcessState::CheckAllEglExtensions(EGLDisplay aDisplay) const
   342 	{
   343 	_LIT8(KEglRSgImage, "EGL_SYMBIAN_PIXMAP_TYPE_RSGIMAGE");		
   344 	_LIT8(KEglKhrImage, "EGL_KHR_image");
   345 	_LIT8(KEglImagePreserved, "EGL_SYMBIAN_image_preserved");
   346 		
   347 	TInt err = CheckForEglExtension(aDisplay, KEglRSgImage);
   348 	
   349 	if (err == KErrNone)
   350 		{
   351 		err = CheckForEglExtension(aDisplay, KEglKhrImage);
   352 		}
   353 	
   354 	if (err == KErrNone)
   355 		{
   356 		err = CheckForEglExtension(aDisplay, KEglImagePreserved);
   357 		}
   358 			
   359 	return err;
   360 	}
   361 
   362 /**
   363 Checks whether the passed EGL extension string is available with this EGL driver.
   364 
   365 @param aDisplay The current EGL display handle.
   366 @param aExtensionString The EGL extension string to search for.
   367 
   368 @return KErrNotSupported is the extension string is not found, KErrNone if it is found successfully.
   369  */
   370 TInt XDirectGdiDriverProcessState::CheckForEglExtension(EGLDisplay aDisplay,
   371 															  const TDesC8& aExtensionString) const
   372 {
   373 	const TText8* extensions = reinterpret_cast<const TText8*>(eglQueryString(aDisplay, EGL_EXTENSIONS));
   374 	
   375 	TBool found = EFalse;	
   376 	if (extensions)
   377 		{		
   378 		TPtrC8 ext(extensions);
   379 		if (ext.Find(aExtensionString) != KErrNotFound)
   380 			{
   381 			found = ETrue;
   382 			}
   383 		}
   384 	
   385 	return (found) ? KErrNone : KErrNotSupported;
   386 	}
   387 
   388 /**
   389 Checks to see that the passed RSgImage exists in the image array. Increases
   390 the reference count on the image if it is found. The calling method should be holding the mutex
   391 iMutex open before calling this method.
   392 
   393 @param aSgImage The RSgImage to check for.
   394 @param aVgImageRet The handle of the VGImage found is returned here.
   395 
   396 @pre The mutex iMutex is held.  
   397 
   398 @return ETrue if the image was found, EFalse otherwise.
   399 
   400 @panic DGDIAdapter 52, if the mutex has not been opened before calling this method. 
   401 	   This panic only happens when _DEBUG_DIRECTGDI is defined.
   402  */
   403 TBool XDirectGdiDriverProcessState::ImageExists(const RSgImage& aSgImage, VGImage& aVgImageRet)
   404 	{
   405 #ifdef _DEBUG_DIRECTGDI
   406 	GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError);
   407 #endif	
   408 	aVgImageRet = VG_INVALID_HANDLE;
   409 	
   410 	CImageSourceData* imageData = FindImage(aSgImage);
   411 	if (imageData)
   412 		{
   413 		aVgImageRet = imageData->VgImage();
   414 		imageData->Open();
   415 		}
   416 
   417 	return (aVgImageRet != VG_INVALID_HANDLE);
   418 	}
   419 
   420 /** 
   421 Searches for an image in the array of images using the passed RSgImage id to find it.
   422 The calling method should be holding the images mutex iMutex open before calling this method.
   423 
   424 @param aSgImage The RSgImage to search for.
   425 
   426 @pre The mutex iMutex is held.
   427 
   428 @return Pointer to the CImageSourceData object held in the images array for the passed
   429 RSgImage if it is found, NULL if the image is not found.
   430 
   431 @panic DGDIAdapter 52, if the mutex has not been opened before calling this method. 
   432 	   This panic only happens when _DEBUG_DIRECTGDI is defined.
   433  */
   434 CImageSourceData* XDirectGdiDriverProcessState::FindImage(const RSgImage& aSgImage)
   435 	{
   436 #ifdef _DEBUG_DIRECTGDI
   437 	GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError);
   438 #endif
   439 	CImageSourceData* imageDataRet = NULL;
   440 	
   441 	for (TInt i = 0; i < iImages.Count() && !imageDataRet; i++)
   442 		{
   443 		CImageSourceData* imageData = reinterpret_cast<CImageSourceData*>(iImages[i]);
   444 		if (imageData->SgImageId() == aSgImage.Id())
   445 			{
   446 			imageDataRet = imageData;
   447 			}
   448 		}
   449 	
   450 	return imageDataRet;
   451 	}
   452 	
   453 /**
   454 Adds an image to the images array by saving its RSgImage id, its EGLImageKHR handle and its VGImage
   455 handle. A new CImageSourceData object is created to hold this information. This CImageSourceData object 
   456 is also used to maintain a reference count on the number of times this image has been shared.
   457 The calling method should be holding the images mutex iMutex open before calling this method.
   458 
   459 @see RemoveImage()
   460 
   461 @param aSgImage The RSgImage to store, only the RSgImage id will be stored.
   462 @param aEglImage The EGLImageKHR to store.
   463 @param aVgImage The VGImage to stord.
   464 
   465 @pre The mutex iMutex is held.
   466 @return KErrNone if the details of the passed image are added to the image array successfully, KErrNoMemory
   467 if memory allocation fails, otherwise one of the system-wide error codes.
   468 
   469 @panic DGDIAdapter 52, if the mutex has not been opened before calling this method. 
   470 	   This panic only happens when _DEBUG_DIRECTGDI is defined.
   471  */
   472 TInt XDirectGdiDriverProcessState::AddImage(const RSgImage& aSgImage, EGLImageKHR aEglImage, VGImage aVgImage)
   473 	{
   474 #ifdef _DEBUG_DIRECTGDI
   475 	GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError);
   476 #endif
   477 	TInt err = KErrNone;
   478 	RHeap *prevHeap = User::SwitchHeap(iLocalHeap);
   479 	
   480 	CImageSourceData* imageData = new CImageSourceData(aSgImage.Id(), aEglImage, aVgImage);
   481 	if (imageData == NULL)
   482 		{
   483 		err = KErrNoMemory;
   484 		}	
   485 	if (err == KErrNone)
   486 		{				
   487 		err = iImages.InsertInAddressOrder(imageData);
   488 		}	
   489 	if (err == KErrNone)
   490 		{
   491 		imageData->Open();
   492 		}
   493 	else
   494 		{
   495 		delete imageData;
   496 		}
   497 		
   498 	User::SwitchHeap(prevHeap);	
   499 	return err;
   500 	}
   501 	
   502 /**
   503 Removes the passed image from the image array by searching for it using its VGImage handle.
   504 If the image is found in the image array, its reference count will be decremented. When the count reaches
   505 zero the image will be removed from the image array and destroyed.
   506 
   507 @see AddImage()
   508 
   509 @param aDisplay The EGL display this image was created with.
   510 @param aVgImage The hande to a VGImage to be removed from the image array.
   511 
   512 @pre The mutex iMutex is held.
   513 
   514 @return KErrNone if the image is removed from the image array successfully, KErrNotFound if the image
   515 cannot be found in the image array, otherwise one of the system-wide error codes.
   516 
   517 @panic DGDIAdapter 52, if the mutex has not been opened before calling this method. 
   518 	   This panic only happens when _DEBUG_DIRECTGDI is defined.
   519  */
   520 TInt XDirectGdiDriverProcessState::RemoveImage(EGLDisplay aDisplay, VGImage aVgImage)
   521 	{
   522 #ifdef _DEBUG_DIRECTGDI
   523 	GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError);
   524 #endif
   525 	TInt err = KErrNone;
   526 	TInt index = -1;
   527 	CImageSourceData* removeImage = NULL;	
   528 	
   529 	for (TInt i = 0; i < iImages.Count() && (index == -1); i++)
   530 		{
   531 		if (CImageSourceData* imageData = reinterpret_cast<CImageSourceData*>(iImages[i]))
   532 			{
   533 			if (imageData->VgImage() == aVgImage)
   534 				{
   535 				// Only remove the image if the reference count is zero
   536 				if (imageData->Close() == 0)					
   537 					removeImage	= imageData;									
   538 				index = i;
   539 				}
   540 			}
   541 		}
   542 			
   543 	if (removeImage)
   544 		{
   545 		// Clean up the EGL and VG images
   546 		vgDestroyImage(removeImage->VgImage());
   547 		iPfnEglDestroyImageKHR(aDisplay, removeImage->EglImage());
   548 		
   549 		RHeap *prevHeap = User::SwitchHeap(iLocalHeap);
   550 		iImages.Remove(index);
   551 		iImages.GranularCompress();
   552 		delete removeImage;
   553 		User::SwitchHeap(prevHeap);
   554 		}
   555 	
   556 	if (index == -1)
   557 		{
   558 		err = KErrNotFound;
   559 		}
   560 	
   561 	return err;
   562 	}