imagingandcamerafws/imagingfws/GifScaler/src/GifScalerBody.cpp
changeset 0 40261b775718
equal deleted inserted replaced
-1:000000000000 0:40261b775718
       
     1 // Copyright (c) 2003-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 "NQColorQuantizer.h"
       
    17 #include "FastColorQuantizer.h"
       
    18 #include "GifScalerBody.h"
       
    19 #include "GifScalerMain.h"
       
    20 #include "gifscaler.h"
       
    21 
       
    22 // Default transparency threshold.
       
    23 const TUint8 KDefaultTransparencyThreshold = 128;
       
    24 
       
    25 // Number of palette entries (8bpp)
       
    26 const TInt KMaxPaletteEntries = 256;
       
    27 
       
    28 
       
    29 /*
       
    30 * NewL
       
    31 * The function NewL constructs a CGifScalerBody
       
    32 *
       
    33 * @return CGifScalerBody*
       
    34 *
       
    35 */
       
    36 CGifScaler::CBody* CGifScaler::CBody::NewL(CFbsBitmap& aSource, CFbsBitmap* aSourceMask, CGifScaler::TOptions aOptions)
       
    37 	{
       
    38 	// Assert valid source bitmap.
       
    39 	__ASSERT_ALWAYS(aSource.Handle() != 0, Panic(ENoSourceBitmap));
       
    40 
       
    41    if(aSource.ExtendedBitmapType()!=KNullUid)
       
    42         {
       
    43         User::Leave(KErrNotSupported);
       
    44         }
       
    45 	
       
    46 	// If any dimension is zero then send back an error.
       
    47 	TSize sourceSize = aSource.SizeInPixels();
       
    48 	if ((sourceSize.iWidth == 0) || (sourceSize.iHeight == 0))
       
    49 		User::Leave(KErrArgument);
       
    50 
       
    51 	if (aSourceMask)
       
    52 		{
       
    53 		// Assert valid source mask bitmap.
       
    54 		__ASSERT_ALWAYS(aSourceMask->Handle() != 0, Panic(ENoSourceMaskBitmap));
       
    55 		
       
    56 		 if(aSourceMask->ExtendedBitmapType()!=KNullUid)
       
    57             {
       
    58             User::Leave(KErrNotSupported);
       
    59             }
       
    60 
       
    61 		// If the mask dimensions are not the same as the source then send back an error.
       
    62 		TSize maskSize = aSourceMask->SizeInPixels();
       
    63 		if (maskSize != sourceSize)
       
    64 			User::Leave(KErrArgument);
       
    65 
       
    66 		// If the mask bitmap isn't in the correct display mode then send back an error.
       
    67 		TDisplayMode maskMode = aSourceMask->DisplayMode();
       
    68 		if ((maskMode != EGray2) && (maskMode != EGray256))
       
    69 			User::Leave(KErrArgument);
       
    70 		}
       
    71 
       
    72 	// If the options are invalid send back an error.
       
    73 	if ((aOptions<CGifScaler::ELowQualityQuantization) || (aOptions>CGifScaler::EMaximumQualityQuantization))
       
    74 		User::Leave(KErrArgument);
       
    75 	
       
    76 	CBody* self = new(ELeave) CBody(aSource, aSourceMask, aOptions);
       
    77 	CleanupStack::PushL(self);
       
    78 	self->ConstructL();
       
    79 
       
    80 	CleanupStack::Pop();
       
    81 	return self;
       
    82 	}
       
    83 
       
    84 
       
    85 /*
       
    86 *
       
    87 * CGifScalerBody
       
    88 * Constructor for this class. Adds itself to <code>CActiveScheduler</code>.
       
    89 *
       
    90 */
       
    91 CGifScaler::CBody::CBody(CFbsBitmap& aSource, CFbsBitmap* aSourceMask, CGifScaler::TOptions aOptions)
       
    92 	: CActive(CActive::EPriorityIdle), iSource(aSource), iSourceMask(aSourceMask), iOptions(aOptions)
       
    93 	{
       
    94 	CActiveScheduler::Add(this);
       
    95 	}
       
    96 
       
    97 
       
    98 /*
       
    99 * ConstructL
       
   100 * Performs second phase of contruction
       
   101 *
       
   102 */
       
   103 void CGifScaler::CBody::ConstructL()
       
   104 	{
       
   105 	ASSERT(iCurrentState == EInactiveState);
       
   106 	ASSERT(iBitmapScaler == NULL);
       
   107 	iBitmapScaler = CBitmapScaler::NewL();
       
   108 	}
       
   109 
       
   110 
       
   111 /*
       
   112 *
       
   113 * ~CGifScalerBody
       
   114 * This is the destructor for the CGifScalerBody 
       
   115 * and is resposible for deallocating all resources 
       
   116 * allocated by the CGifScalerBody
       
   117 *
       
   118 */
       
   119 CGifScaler::CBody::~CBody()
       
   120 	{
       
   121 	// Parent object should have cancelled the scaling,
       
   122 	// so assert that we are not active here.
       
   123 	ASSERT(!IsActive());
       
   124 
       
   125 	ASSERT(iCurrentState == EInactiveState);
       
   126 	ASSERT(iDestinationMask == NULL);
       
   127 	delete iBitmapScaler;
       
   128 	}
       
   129 
       
   130 // Implementation of CGifScaler::Scale.
       
   131 void CGifScaler::CBody::Scale(TRequestStatus* aStatus, CFbsBitmap& aDestination, CPalette& aPalette, TBool aMaintainAspectRatio)
       
   132 	{
       
   133 	Scale(aStatus, aDestination, aPalette, KDefaultTransparencyThreshold, aMaintainAspectRatio);
       
   134 	}
       
   135 
       
   136 // Implementation of CGifScaler::Scale (second version).
       
   137 void CGifScaler::CBody::Scale(TRequestStatus* aStatus, CFbsBitmap& aDestination, CPalette& aPalette, TUint8 aTransparencyThreshold, TBool aMaintainAspectRatio)
       
   138 	{
       
   139 	// Panic if aStatus is NULL.
       
   140 	__ASSERT_ALWAYS(aStatus != NULL, Panic(EBadRequestStatus));
       
   141 	iRequestStatus = aStatus;
       
   142 	*iRequestStatus = KRequestPending;
       
   143 
       
   144 	// Panic if the target bitmap hasn't been created.
       
   145 	__ASSERT_ALWAYS(aDestination.Handle() != 0, Panic(ENoDestinationBitmap));
       
   146 
       
   147 	// If the target bitmap is not 256 color then send back an error.
       
   148 	if (aDestination.DisplayMode() != EColor256)
       
   149 		{
       
   150 		RequestComplete(KErrArgument);
       
   151 		return;
       
   152 		}
       
   153 	
       
   154    if(aDestination.ExtendedBitmapType()!=KNullUid)
       
   155         {
       
   156         RequestComplete(KErrNotSupported);
       
   157         return;
       
   158         }
       
   159 
       
   160 	// If the number of palette entries is not correct then send back an error.
       
   161 	if (aPalette.Entries() != KMaxPaletteEntries)
       
   162 		{
       
   163 		RequestComplete(KErrArgument);
       
   164 		return;
       
   165 		}
       
   166 	
       
   167 	// Current state must be inactive.
       
   168 	if (iCurrentState != EInactiveState)
       
   169 		{
       
   170 		RequestComplete(KErrGeneral);
       
   171 		return;
       
   172 		}
       
   173 
       
   174 	// If any dimension is zero then send back KErrNone.
       
   175 	TSize destSize = aDestination.SizeInPixels();
       
   176 	if ((destSize.iWidth == 0) || (destSize.iHeight == 0))
       
   177 		{
       
   178 		RequestComplete(KErrNone);
       
   179 		return;
       
   180 		}
       
   181 
       
   182 	iPalette = &aPalette;
       
   183 	iPalette->Clear();
       
   184 	iDestination = &aDestination;
       
   185 	iTransparencyThreshold = aTransparencyThreshold;
       
   186 	iMaintainAspectRatio = aMaintainAspectRatio;
       
   187 
       
   188 	TInt err;
       
   189 	if (iSourceMask != NULL)
       
   190 		{
       
   191 		ASSERT(iDestinationMask == NULL);
       
   192 		iDestinationMask = new CFbsBitmap;
       
   193 		if (!iDestinationMask)
       
   194 			{
       
   195 			RequestComplete(KErrNoMemory);
       
   196 			return;
       
   197 			}
       
   198 
       
   199 		err = iDestinationMask->Create(destSize, EGray256);
       
   200 		if (err != KErrNone)
       
   201 			{
       
   202 			delete iDestinationMask;
       
   203 			iDestinationMask = NULL;
       
   204 		
       
   205 			RequestComplete(err);
       
   206 			return;
       
   207 			}
       
   208 		}
       
   209 
       
   210 	ASSERT(iIntermediate == NULL);
       
   211 	iIntermediate = new CFbsBitmap;
       
   212 	if (!iIntermediate)
       
   213 		{
       
   214 		delete iDestinationMask;
       
   215 		iDestinationMask = NULL;
       
   216 			
       
   217 		RequestComplete(KErrNoMemory);
       
   218 		return;
       
   219 		}
       
   220 
       
   221 	if (destSize == iSource.SizeInPixels())
       
   222 		{
       
   223 		// no scaling required
       
   224 		iIntermediate->Duplicate(iSource.Handle());
       
   225 		if (iSourceMask != NULL)
       
   226 			{
       
   227 			iDestinationMask->Duplicate(iSourceMask->Handle());
       
   228 			}
       
   229 		iCurrentState = EStartColorQuantization;
       
   230 
       
   231 		SelfComplete(KErrNone);
       
   232 		return;
       
   233 		}
       
   234 
       
   235 	err = iIntermediate->Create(destSize, EColor16M);
       
   236 	if (err != KErrNone)
       
   237 		{
       
   238 		delete iDestinationMask;
       
   239 		iDestinationMask = NULL;
       
   240 
       
   241 		delete iIntermediate;
       
   242 		iIntermediate = NULL;
       
   243 		
       
   244 		RequestComplete(err);
       
   245 		return;
       
   246 		}
       
   247 
       
   248 	iCurrentState = EScalingState;
       
   249 
       
   250 	// Start by scaling the source.
       
   251 	iBitmapScaler->Scale(&iStatus, iSource, *iIntermediate, iMaintainAspectRatio);
       
   252 	SetActive();
       
   253 	}
       
   254 
       
   255 
       
   256 /*
       
   257 *
       
   258 * RunL
       
   259 * Handle the next chunk of scaling/color quantization.
       
   260 */
       
   261 void CGifScaler::CBody::RunL()
       
   262 	{
       
   263 	if (iStatus.Int() != KErrNone)
       
   264 		{
       
   265 		iCurrentState = ECleanUpPending;
       
   266 		}
       
   267 
       
   268 	switch (iCurrentState)
       
   269 		{
       
   270 		case EScalingState:
       
   271 			// Scaling of source successful, so
       
   272 			// start scaling the mask if there is one.
       
   273 			iCurrentState = EStartColorQuantization;
       
   274 			if (iSourceMask)
       
   275 				{
       
   276 				iBitmapScaler->Scale(&iStatus, *iSourceMask, *iDestinationMask);
       
   277 				SetActive();
       
   278 				break;
       
   279 				}
       
   280 
       
   281 		case EStartColorQuantization:
       
   282 			// Scaling is done,
       
   283 			// so start color quantization if required.
       
   284 			{
       
   285 			// Resize the destination.
       
   286 			// (The scaler may have changed the size)
       
   287 			TSize intermediateSize = iIntermediate->SizeInPixels();
       
   288 			if (intermediateSize != iDestination->SizeInPixels())
       
   289 				{
       
   290 				if ((intermediateSize.iWidth == 0) || (intermediateSize.iHeight == 0))
       
   291 					{
       
   292 					// Complete the operation if the scaler
       
   293 					// has reduced either dimension to zero.
       
   294 					iCurrentState = ECleanUpState;
       
   295 					break;
       
   296 					}
       
   297 
       
   298 				User::LeaveIfError(iDestination->Resize(intermediateSize));
       
   299 				if (iDestinationMask)
       
   300 					{
       
   301 					User::LeaveIfError(iDestinationMask->Resize(intermediateSize));
       
   302 					}
       
   303 				}
       
   304 
       
   305 			// Initialize settings for color quantizer.
       
   306 			TInt numPaletteEntries = iSourceMask ? KMaxPaletteEntries-1 : KMaxPaletteEntries;
       
   307 			TInt sampleFactor = 1;
       
   308 			switch (iOptions)
       
   309 				{
       
   310 				case CGifScaler::ELowQualityQuantization:
       
   311 					sampleFactor = 10;
       
   312 					break;
       
   313 
       
   314 				case CGifScaler::EMediumQualityQuantization:
       
   315 					sampleFactor = 6;
       
   316 					break;
       
   317 
       
   318 				case CGifScaler::EHighQualityQuantization:
       
   319 					sampleFactor = 3;
       
   320 					break;
       
   321 
       
   322 				case CGifScaler::EMaximumQualityQuantization:
       
   323 					sampleFactor = 1;
       
   324 					break;
       
   325 
       
   326 				default:
       
   327 					ASSERT(0);
       
   328 				}
       
   329 
       
   330 			// decide which color quantizer to use
       
   331 			// CFastColorQuantiser can be used if the number of unique colors in
       
   332 			// the image data is less than numPaletteEntries
       
   333 			TBool useFastColorQuantiser = EFalse;
       
   334 			TInt totalPixels = intermediateSize.iWidth * intermediateSize.iHeight;
       
   335 			TInt nonTransparentPixels = totalPixels;
       
   336 			if (totalPixels <= numPaletteEntries)
       
   337 				{
       
   338 				useFastColorQuantiser = ETrue;
       
   339 				}			
       
   340 			else
       
   341 				{
       
   342 				// check the number of unique colors in the image data
       
   343 				// if a transparency mask is supplied:
       
   344 				// - exclude transparent pixels from the count
       
   345 				// - update nonTransparentPixels
       
   346 				
       
   347 				CPalette* colorPalette = CPalette::NewL(numPaletteEntries);
       
   348 
       
   349 				TPoint pixelPos(0, 0);
       
   350 				TRgb pixelColor;
       
   351 				TInt paletteIndex = 0;
       
   352 				TBool tooManyColors = EFalse;
       
   353 				TBool finished = EFalse;
       
   354 				TBitmapUtil bitmapUtil(iIntermediate);
       
   355 				bitmapUtil.Begin(pixelPos);
       
   356 
       
   357 				TInt scanlineLengthMask = 0;
       
   358 				TUint8* basePosMask = NULL;				
       
   359 
       
   360 				if (iDestinationMask)
       
   361 					{
       
   362 					scanlineLengthMask = CFbsBitmap::ScanLineLength(intermediateSize.iWidth, iDestinationMask->DisplayMode());
       
   363 					basePosMask = reinterpret_cast<TUint8*>(iDestinationMask->DataAddress());
       
   364 					}
       
   365 				
       
   366 				for (; (pixelPos.iY < intermediateSize.iHeight) && !finished; pixelPos.iY++)
       
   367 						{
       
   368 						pixelPos.iX = 0;
       
   369 						bitmapUtil.SetPos(pixelPos);
       
   370 						for (; (pixelPos.iX < intermediateSize.iWidth) && !finished; pixelPos.iX++)
       
   371 							{
       
   372 							pixelColor.SetInternal(bitmapUtil.GetPixel());
       
   373 							TBool pixelTransparent = iDestinationMask && (basePosMask[pixelPos.iX] <= iTransparencyThreshold);
       
   374 
       
   375 							if (pixelTransparent)
       
   376 								{
       
   377 								nonTransparentPixels--;
       
   378 								}
       
   379 							else if (colorPalette->NearestEntry(pixelColor) != pixelColor)
       
   380 								{
       
   381 								if (paletteIndex < numPaletteEntries)
       
   382 									{
       
   383 									// current pixel color is not in the palette so add it
       
   384 									colorPalette->SetEntry(paletteIndex, pixelColor);
       
   385 									paletteIndex++;
       
   386 									}
       
   387 								else
       
   388 									{
       
   389 									// there are more colors in the image data than palette spaces
       
   390 									tooManyColors = ETrue;
       
   391 									if (!iDestinationMask)
       
   392 										{
       
   393 										// not counting nontransparent pixels so can finish
       
   394 										finished = ETrue;
       
   395 										}
       
   396 									}
       
   397 								}
       
   398 
       
   399 							bitmapUtil.IncXPos();
       
   400 							}
       
   401 
       
   402 						basePosMask += scanlineLengthMask;
       
   403 						}
       
   404 					bitmapUtil.End();
       
   405 					delete colorPalette;
       
   406 				
       
   407 					if (!tooManyColors)
       
   408 						{
       
   409 						useFastColorQuantiser = ETrue;
       
   410 						}
       
   411 					}
       
   412 
       
   413 
       
   414 				// Create the color quantizer.
       
   415 				if (useFastColorQuantiser)
       
   416 					{
       
   417 					// The number of pixels in the image is small,
       
   418 					// so we can color quantize the image perfectly and quickly.
       
   419 					iColorQuantizer = CFastColorQuantizer::NewL(iPalette, iTransparencyThreshold);
       
   420 					}
       
   421 				else
       
   422 					{
       
   423 					iColorQuantizer = CNQColorQuantizer::NewL(iPalette, numPaletteEntries, sampleFactor, iTransparencyThreshold);
       
   424 					}
       
   425 					
       
   426 				// Kick off the color quantization.
       
   427 				iColorQuantizer->Quantize(&iStatus, *iIntermediate, *iDestination, iDestinationMask, nonTransparentPixels);
       
   428 
       
   429 				// Move to the cleanup state when the quantization is done.
       
   430 				iCurrentState = ECleanUpPending;
       
   431 				SetActive();
       
   432 				}
       
   433 
       
   434 			break;
       
   435 
       
   436 		case ECleanUpPending:
       
   437 			// All operations are complete,
       
   438 			// so move to the cleanup state.
       
   439 			iCurrentState = ECleanUpState;
       
   440 			break;
       
   441 
       
   442 		default:
       
   443 			ASSERT(0);
       
   444 			break;
       
   445 		}
       
   446 
       
   447 	if (iCurrentState == ECleanUpState)
       
   448 		{
       
   449 		CleanUp();
       
   450 		iCurrentState = EScalingComplete;
       
   451 		}
       
   452 
       
   453 	if (iCurrentState == EScalingComplete)
       
   454 		{
       
   455 		RequestComplete(iStatus.Int());
       
   456 		iCurrentState = EInactiveState;
       
   457 		}
       
   458 	}
       
   459 
       
   460 /*
       
   461 *
       
   462 * RunError
       
   463 * Handle leaving errors from RunL.
       
   464 */
       
   465 TInt CGifScaler::CBody::RunError(TInt aError)
       
   466 	{
       
   467 	CleanUp();
       
   468 	RequestComplete(aError);
       
   469 	iCurrentState = EInactiveState;
       
   470 
       
   471 	return KErrNone;
       
   472 	}
       
   473 	
       
   474 /*
       
   475 *
       
   476 * DoCancel
       
   477 * Called by active object to prematurely terminate this bitmap scaling.
       
   478 *
       
   479 */
       
   480 void CGifScaler::CBody::DoCancel()
       
   481 	{
       
   482 	// Cancel scaling.
       
   483 	ASSERT(iBitmapScaler);
       
   484 	iBitmapScaler->Cancel();
       
   485 
       
   486 	// Cancel color quantization.
       
   487 	if (iColorQuantizer)
       
   488 		iColorQuantizer->Cancel();
       
   489 	
       
   490 	CleanUp();
       
   491 	iCurrentState = EInactiveState;
       
   492 	RequestComplete(KErrCancel);
       
   493 	}
       
   494 
       
   495 /*
       
   496 *
       
   497 * CleanUp
       
   498 * Delete all the memory allocated during scaling.
       
   499 *
       
   500 */
       
   501 void CGifScaler::CBody::CleanUp()
       
   502 	{
       
   503 	// Delete the intermediate.
       
   504 	delete iIntermediate;
       
   505 	iIntermediate = NULL;
       
   506 
       
   507 	// Delete the color quantizer.
       
   508 	delete iColorQuantizer;
       
   509 	iColorQuantizer = NULL;
       
   510 
       
   511 	// Delete the destination mask.
       
   512 	delete iDestinationMask;
       
   513 	iDestinationMask = NULL;
       
   514 	}
       
   515 	
       
   516 /*
       
   517 * 
       
   518 * RequestComplete
       
   519 * @param "TInt aReason"
       
   520 *
       
   521 */
       
   522 void CGifScaler::CBody::RequestComplete(TInt aReason)
       
   523 	{
       
   524 	TRequestStatus* stat = iRequestStatus;
       
   525 	User::RequestComplete(stat, aReason);
       
   526 	}
       
   527 
       
   528 /*
       
   529 *
       
   530 * SelfComplete
       
   531 * This function activates the active object and 
       
   532 * signals completion of the current asynchronous operation
       
   533 *
       
   534 * @param "TInt aReason"
       
   535 *
       
   536 */
       
   537 void CGifScaler::CBody::SelfComplete(TInt aReason)
       
   538 	{
       
   539 	SetActive();
       
   540 
       
   541 	TRequestStatus* stat = &iStatus;
       
   542 	User::RequestComplete(stat, aReason);
       
   543 	}