--- /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);
+ }