imagingandcamerafws/imagingfws/GifScaler/src/GifScalerBody.cpp
changeset 0 40261b775718
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagingandcamerafws/imagingfws/GifScaler/src/GifScalerBody.cpp	Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,543 @@
+// Copyright (c) 2003-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 "NQColorQuantizer.h"
+#include "FastColorQuantizer.h"
+#include "GifScalerBody.h"
+#include "GifScalerMain.h"
+#include "gifscaler.h"
+
+// Default transparency threshold.
+const TUint8 KDefaultTransparencyThreshold = 128;
+
+// Number of palette entries (8bpp)
+const TInt KMaxPaletteEntries = 256;
+
+
+/*
+* NewL
+* The function NewL constructs a CGifScalerBody
+*
+* @return CGifScalerBody*
+*
+*/
+CGifScaler::CBody* CGifScaler::CBody::NewL(CFbsBitmap& aSource, CFbsBitmap* aSourceMask, CGifScaler::TOptions aOptions)
+	{
+	// Assert valid source bitmap.
+	__ASSERT_ALWAYS(aSource.Handle() != 0, Panic(ENoSourceBitmap));
+
+   if(aSource.ExtendedBitmapType()!=KNullUid)
+        {
+        User::Leave(KErrNotSupported);
+        }
+	
+	// If any dimension is zero then send back an error.
+	TSize sourceSize = aSource.SizeInPixels();
+	if ((sourceSize.iWidth == 0) || (sourceSize.iHeight == 0))
+		User::Leave(KErrArgument);
+
+	if (aSourceMask)
+		{
+		// Assert valid source mask bitmap.
+		__ASSERT_ALWAYS(aSourceMask->Handle() != 0, Panic(ENoSourceMaskBitmap));
+		
+		 if(aSourceMask->ExtendedBitmapType()!=KNullUid)
+            {
+            User::Leave(KErrNotSupported);
+            }
+
+		// If the mask dimensions are not the same as the source then send back an error.
+		TSize maskSize = aSourceMask->SizeInPixels();
+		if (maskSize != sourceSize)
+			User::Leave(KErrArgument);
+
+		// If the mask bitmap isn't in the correct display mode then send back an error.
+		TDisplayMode maskMode = aSourceMask->DisplayMode();
+		if ((maskMode != EGray2) && (maskMode != EGray256))
+			User::Leave(KErrArgument);
+		}
+
+	// If the options are invalid send back an error.
+	if ((aOptions<CGifScaler::ELowQualityQuantization) || (aOptions>CGifScaler::EMaximumQualityQuantization))
+		User::Leave(KErrArgument);
+	
+	CBody* self = new(ELeave) CBody(aSource, aSourceMask, aOptions);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+/*
+*
+* CGifScalerBody
+* Constructor for this class. Adds itself to <code>CActiveScheduler</code>.
+*
+*/
+CGifScaler::CBody::CBody(CFbsBitmap& aSource, CFbsBitmap* aSourceMask, CGifScaler::TOptions aOptions)
+	: CActive(CActive::EPriorityIdle), iSource(aSource), iSourceMask(aSourceMask), iOptions(aOptions)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+/*
+* ConstructL
+* Performs second phase of contruction
+*
+*/
+void CGifScaler::CBody::ConstructL()
+	{
+	ASSERT(iCurrentState == EInactiveState);
+	ASSERT(iBitmapScaler == NULL);
+	iBitmapScaler = CBitmapScaler::NewL();
+	}
+
+
+/*
+*
+* ~CGifScalerBody
+* This is the destructor for the CGifScalerBody 
+* and is resposible for deallocating all resources 
+* allocated by the CGifScalerBody
+*
+*/
+CGifScaler::CBody::~CBody()
+	{
+	// Parent object should have cancelled the scaling,
+	// so assert that we are not active here.
+	ASSERT(!IsActive());
+
+	ASSERT(iCurrentState == EInactiveState);
+	ASSERT(iDestinationMask == NULL);
+	delete iBitmapScaler;
+	}
+
+// Implementation of CGifScaler::Scale.
+void CGifScaler::CBody::Scale(TRequestStatus* aStatus, CFbsBitmap& aDestination, CPalette& aPalette, TBool aMaintainAspectRatio)
+	{
+	Scale(aStatus, aDestination, aPalette, KDefaultTransparencyThreshold, aMaintainAspectRatio);
+	}
+
+// Implementation of CGifScaler::Scale (second version).
+void CGifScaler::CBody::Scale(TRequestStatus* aStatus, CFbsBitmap& aDestination, CPalette& aPalette, TUint8 aTransparencyThreshold, TBool aMaintainAspectRatio)
+	{
+	// Panic if aStatus is NULL.
+	__ASSERT_ALWAYS(aStatus != NULL, Panic(EBadRequestStatus));
+	iRequestStatus = aStatus;
+	*iRequestStatus = KRequestPending;
+
+	// Panic if the target bitmap hasn't been created.
+	__ASSERT_ALWAYS(aDestination.Handle() != 0, Panic(ENoDestinationBitmap));
+
+	// If the target bitmap is not 256 color then send back an error.
+	if (aDestination.DisplayMode() != EColor256)
+		{
+		RequestComplete(KErrArgument);
+		return;
+		}
+	
+   if(aDestination.ExtendedBitmapType()!=KNullUid)
+        {
+        RequestComplete(KErrNotSupported);
+        return;
+        }
+
+	// If the number of palette entries is not correct then send back an error.
+	if (aPalette.Entries() != KMaxPaletteEntries)
+		{
+		RequestComplete(KErrArgument);
+		return;
+		}
+	
+	// Current state must be inactive.
+	if (iCurrentState != EInactiveState)
+		{
+		RequestComplete(KErrGeneral);
+		return;
+		}
+
+	// If any dimension is zero then send back KErrNone.
+	TSize destSize = aDestination.SizeInPixels();
+	if ((destSize.iWidth == 0) || (destSize.iHeight == 0))
+		{
+		RequestComplete(KErrNone);
+		return;
+		}
+
+	iPalette = &aPalette;
+	iPalette->Clear();
+	iDestination = &aDestination;
+	iTransparencyThreshold = aTransparencyThreshold;
+	iMaintainAspectRatio = aMaintainAspectRatio;
+
+	TInt err;
+	if (iSourceMask != NULL)
+		{
+		ASSERT(iDestinationMask == NULL);
+		iDestinationMask = new CFbsBitmap;
+		if (!iDestinationMask)
+			{
+			RequestComplete(KErrNoMemory);
+			return;
+			}
+
+		err = iDestinationMask->Create(destSize, EGray256);
+		if (err != KErrNone)
+			{
+			delete iDestinationMask;
+			iDestinationMask = NULL;
+		
+			RequestComplete(err);
+			return;
+			}
+		}
+
+	ASSERT(iIntermediate == NULL);
+	iIntermediate = new CFbsBitmap;
+	if (!iIntermediate)
+		{
+		delete iDestinationMask;
+		iDestinationMask = NULL;
+			
+		RequestComplete(KErrNoMemory);
+		return;
+		}
+
+	if (destSize == iSource.SizeInPixels())
+		{
+		// no scaling required
+		iIntermediate->Duplicate(iSource.Handle());
+		if (iSourceMask != NULL)
+			{
+			iDestinationMask->Duplicate(iSourceMask->Handle());
+			}
+		iCurrentState = EStartColorQuantization;
+
+		SelfComplete(KErrNone);
+		return;
+		}
+
+	err = iIntermediate->Create(destSize, EColor16M);
+	if (err != KErrNone)
+		{
+		delete iDestinationMask;
+		iDestinationMask = NULL;
+
+		delete iIntermediate;
+		iIntermediate = NULL;
+		
+		RequestComplete(err);
+		return;
+		}
+
+	iCurrentState = EScalingState;
+
+	// Start by scaling the source.
+	iBitmapScaler->Scale(&iStatus, iSource, *iIntermediate, iMaintainAspectRatio);
+	SetActive();
+	}
+
+
+/*
+*
+* RunL
+* Handle the next chunk of scaling/color quantization.
+*/
+void CGifScaler::CBody::RunL()
+	{
+	if (iStatus.Int() != KErrNone)
+		{
+		iCurrentState = ECleanUpPending;
+		}
+
+	switch (iCurrentState)
+		{
+		case EScalingState:
+			// Scaling of source successful, so
+			// start scaling the mask if there is one.
+			iCurrentState = EStartColorQuantization;
+			if (iSourceMask)
+				{
+				iBitmapScaler->Scale(&iStatus, *iSourceMask, *iDestinationMask);
+				SetActive();
+				break;
+				}
+
+		case EStartColorQuantization:
+			// Scaling is done,
+			// so start color quantization if required.
+			{
+			// Resize the destination.
+			// (The scaler may have changed the size)
+			TSize intermediateSize = iIntermediate->SizeInPixels();
+			if (intermediateSize != iDestination->SizeInPixels())
+				{
+				if ((intermediateSize.iWidth == 0) || (intermediateSize.iHeight == 0))
+					{
+					// Complete the operation if the scaler
+					// has reduced either dimension to zero.
+					iCurrentState = ECleanUpState;
+					break;
+					}
+
+				User::LeaveIfError(iDestination->Resize(intermediateSize));
+				if (iDestinationMask)
+					{
+					User::LeaveIfError(iDestinationMask->Resize(intermediateSize));
+					}
+				}
+
+			// Initialize settings for color quantizer.
+			TInt numPaletteEntries = iSourceMask ? KMaxPaletteEntries-1 : KMaxPaletteEntries;
+			TInt sampleFactor = 1;
+			switch (iOptions)
+				{
+				case CGifScaler::ELowQualityQuantization:
+					sampleFactor = 10;
+					break;
+
+				case CGifScaler::EMediumQualityQuantization:
+					sampleFactor = 6;
+					break;
+
+				case CGifScaler::EHighQualityQuantization:
+					sampleFactor = 3;
+					break;
+
+				case CGifScaler::EMaximumQualityQuantization:
+					sampleFactor = 1;
+					break;
+
+				default:
+					ASSERT(0);
+				}
+
+			// decide which color quantizer to use
+			// CFastColorQuantiser can be used if the number of unique colors in
+			// the image data is less than numPaletteEntries
+			TBool useFastColorQuantiser = EFalse;
+			TInt totalPixels = intermediateSize.iWidth * intermediateSize.iHeight;
+			TInt nonTransparentPixels = totalPixels;
+			if (totalPixels <= numPaletteEntries)
+				{
+				useFastColorQuantiser = ETrue;
+				}			
+			else
+				{
+				// check the number of unique colors in the image data
+				// if a transparency mask is supplied:
+				// - exclude transparent pixels from the count
+				// - update nonTransparentPixels
+				
+				CPalette* colorPalette = CPalette::NewL(numPaletteEntries);
+
+				TPoint pixelPos(0, 0);
+				TRgb pixelColor;
+				TInt paletteIndex = 0;
+				TBool tooManyColors = EFalse;
+				TBool finished = EFalse;
+				TBitmapUtil bitmapUtil(iIntermediate);
+				bitmapUtil.Begin(pixelPos);
+
+				TInt scanlineLengthMask = 0;
+				TUint8* basePosMask = NULL;				
+
+				if (iDestinationMask)
+					{
+					scanlineLengthMask = CFbsBitmap::ScanLineLength(intermediateSize.iWidth, iDestinationMask->DisplayMode());
+					basePosMask = reinterpret_cast<TUint8*>(iDestinationMask->DataAddress());
+					}
+				
+				for (; (pixelPos.iY < intermediateSize.iHeight) && !finished; pixelPos.iY++)
+						{
+						pixelPos.iX = 0;
+						bitmapUtil.SetPos(pixelPos);
+						for (; (pixelPos.iX < intermediateSize.iWidth) && !finished; pixelPos.iX++)
+							{
+							pixelColor.SetInternal(bitmapUtil.GetPixel());
+							TBool pixelTransparent = iDestinationMask && (basePosMask[pixelPos.iX] <= iTransparencyThreshold);
+
+							if (pixelTransparent)
+								{
+								nonTransparentPixels--;
+								}
+							else if (colorPalette->NearestEntry(pixelColor) != pixelColor)
+								{
+								if (paletteIndex < numPaletteEntries)
+									{
+									// current pixel color is not in the palette so add it
+									colorPalette->SetEntry(paletteIndex, pixelColor);
+									paletteIndex++;
+									}
+								else
+									{
+									// there are more colors in the image data than palette spaces
+									tooManyColors = ETrue;
+									if (!iDestinationMask)
+										{
+										// not counting nontransparent pixels so can finish
+										finished = ETrue;
+										}
+									}
+								}
+
+							bitmapUtil.IncXPos();
+							}
+
+						basePosMask += scanlineLengthMask;
+						}
+					bitmapUtil.End();
+					delete colorPalette;
+				
+					if (!tooManyColors)
+						{
+						useFastColorQuantiser = ETrue;
+						}
+					}
+
+
+				// Create the color quantizer.
+				if (useFastColorQuantiser)
+					{
+					// The number of pixels in the image is small,
+					// so we can color quantize the image perfectly and quickly.
+					iColorQuantizer = CFastColorQuantizer::NewL(iPalette, iTransparencyThreshold);
+					}
+				else
+					{
+					iColorQuantizer = CNQColorQuantizer::NewL(iPalette, numPaletteEntries, sampleFactor, iTransparencyThreshold);
+					}
+					
+				// Kick off the color quantization.
+				iColorQuantizer->Quantize(&iStatus, *iIntermediate, *iDestination, iDestinationMask, nonTransparentPixels);
+
+				// Move to the cleanup state when the quantization is done.
+				iCurrentState = ECleanUpPending;
+				SetActive();
+				}
+
+			break;
+
+		case ECleanUpPending:
+			// All operations are complete,
+			// so move to the cleanup state.
+			iCurrentState = ECleanUpState;
+			break;
+
+		default:
+			ASSERT(0);
+			break;
+		}
+
+	if (iCurrentState == ECleanUpState)
+		{
+		CleanUp();
+		iCurrentState = EScalingComplete;
+		}
+
+	if (iCurrentState == EScalingComplete)
+		{
+		RequestComplete(iStatus.Int());
+		iCurrentState = EInactiveState;
+		}
+	}
+
+/*
+*
+* RunError
+* Handle leaving errors from RunL.
+*/
+TInt CGifScaler::CBody::RunError(TInt aError)
+	{
+	CleanUp();
+	RequestComplete(aError);
+	iCurrentState = EInactiveState;
+
+	return KErrNone;
+	}
+	
+/*
+*
+* DoCancel
+* Called by active object to prematurely terminate this bitmap scaling.
+*
+*/
+void CGifScaler::CBody::DoCancel()
+	{
+	// Cancel scaling.
+	ASSERT(iBitmapScaler);
+	iBitmapScaler->Cancel();
+
+	// Cancel color quantization.
+	if (iColorQuantizer)
+		iColorQuantizer->Cancel();
+	
+	CleanUp();
+	iCurrentState = EInactiveState;
+	RequestComplete(KErrCancel);
+	}
+
+/*
+*
+* CleanUp
+* Delete all the memory allocated during scaling.
+*
+*/
+void CGifScaler::CBody::CleanUp()
+	{
+	// Delete the intermediate.
+	delete iIntermediate;
+	iIntermediate = NULL;
+
+	// Delete the color quantizer.
+	delete iColorQuantizer;
+	iColorQuantizer = NULL;
+
+	// Delete the destination mask.
+	delete iDestinationMask;
+	iDestinationMask = NULL;
+	}
+	
+/*
+* 
+* RequestComplete
+* @param "TInt aReason"
+*
+*/
+void CGifScaler::CBody::RequestComplete(TInt aReason)
+	{
+	TRequestStatus* stat = iRequestStatus;
+	User::RequestComplete(stat, aReason);
+	}
+
+/*
+*
+* SelfComplete
+* This function activates the active object and 
+* signals completion of the current asynchronous operation
+*
+* @param "TInt aReason"
+*
+*/
+void CGifScaler::CBody::SelfComplete(TInt aReason)
+	{
+	SetActive();
+
+	TRequestStatus* stat = &iStatus;
+	User::RequestComplete(stat, aReason);
+	}