screengrabber/src/SGGifAnimator.cpp
branchRCL_3
changeset 21 b3cee849fa46
parent 20 48060abbbeaf
child 22 fad26422216a
equal deleted inserted replaced
20:48060abbbeaf 21:b3cee849fa46
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 #include "SGGifAnimator.h"
       
    21 
       
    22 #include <s32file.h>
       
    23 #include <fbs.h>
       
    24 #include <gdi.h>
       
    25 #include <imageconversion.h>
       
    26 #include <aknenv.h>
       
    27 #include <AknGlobalProgressDialog.h>
       
    28 #include <AknQueryDialog.h> 
       
    29 #include <avkon.rsg>
       
    30 
       
    31 _LIT(KSavingText, "Saving");
       
    32     
       
    33 // ---------------------------------------------------------------------------
       
    34 
       
    35 TInt CGifAnimator::CreateGifAnimation(const TDesC& aFileName, TSize aDimensions, CVideoFrameArray* aVideoFrameArray)
       
    36     {
       
    37 	CGifAnimator* self = new(ELeave) CGifAnimator;
       
    38 	CleanupStack::PushL(self);
       
    39 	TRAPD(err, self->StartL(aFileName, aDimensions, aVideoFrameArray));
       
    40 	CleanupStack::PopAndDestroy();
       
    41 	return err;
       
    42     }
       
    43 
       
    44 // ---------------------------------------------------------------------------
       
    45 
       
    46 CGifAnimator::CGifAnimator() : CActive(EPriorityStandard)
       
    47     {
       
    48     }
       
    49 
       
    50 // ---------------------------------------------------------------------------
       
    51 
       
    52 void CGifAnimator::StartL(const TDesC& aFileName, const TSize& aDimensions, CVideoFrameArray* aVideoFrameArray)
       
    53     {
       
    54     __ASSERT_ALWAYS(aFileName.Length() > 0, User::Panic(_L("GifAnim"), 100));
       
    55     __ASSERT_ALWAYS(aDimensions.iHeight > 0, User::Panic(_L("GifAnim"), 101));
       
    56     __ASSERT_ALWAYS(aDimensions.iWidth > 0, User::Panic(_L("GifAnim"), 102));
       
    57     __ASSERT_ALWAYS(aVideoFrameArray != NULL, User::Panic(_L("GifAnim"), 103));
       
    58     
       
    59     CActiveScheduler::Add(this);
       
    60     
       
    61     // show a progress dialog
       
    62     iSavingProgressDialog = CSavingProgressDialog::NewL(this);
       
    63     iSavingProgressDialog->StartDisplayingL(KSavingText, aVideoFrameArray->Count()-1);
       
    64 
       
    65     // open the file for writing
       
    66     User::LeaveIfError(iFs.Connect());
       
    67     User::LeaveIfError(iOutFile.Replace(iFs, aFileName, EFileWrite));
       
    68     
       
    69     // write header
       
    70     WriteHeaderL(aDimensions);
       
    71     
       
    72     // process each frame of the animation
       
    73     for (TInt i=0; i<aVideoFrameArray->Count(); i++)
       
    74         {
       
    75         // write headers and raster block
       
    76         TVideoFrame frame = aVideoFrameArray->At(i);
       
    77         WriteGraphicControlL(frame);
       
    78         CFbsBitmap* bitmap = GetBitmapLC(frame, aDimensions);
       
    79         WriteImageDescriptorL(frame);
       
    80         WriteRasterDataL(bitmap);
       
    81         CleanupStack::PopAndDestroy(); //bitmap
       
    82 
       
    83         // update the progress bar
       
    84         iSavingProgressDialog->IncreaseProgressValueWithOne();
       
    85         }
       
    86 
       
    87     // write footer and finish
       
    88     WriteFooterL();
       
    89     FinishL();
       
    90     
       
    91     // remove the progress dialog from the screen
       
    92     iSavingProgressDialog->ProcessFinished();
       
    93     }    
       
    94 
       
    95 // ---------------------------------------------------------------------------
       
    96 
       
    97 CGifAnimator::~CGifAnimator()
       
    98     {
       
    99     Cancel();
       
   100     
       
   101     if (iImageEncoder)
       
   102         delete iImageEncoder;
       
   103     
       
   104     if (iGIFImageData)
       
   105         delete iGIFImageData;
       
   106     
       
   107     delete iSavingProgressDialog;
       
   108     }
       
   109 
       
   110 // ---------------------------------------------------------------------------
       
   111 
       
   112 void CGifAnimator::WriteHeaderL(const TSize& aDimensions)
       
   113     {
       
   114     WriteInt8L('G');
       
   115     WriteInt8L('I');
       
   116     WriteInt8L('F');
       
   117     WriteInt8L('8');
       
   118     WriteInt8L('9');
       
   119     WriteInt8L('a');
       
   120 
       
   121     WriteInt16L(aDimensions.iWidth);  // width of animation
       
   122     WriteInt16L(aDimensions.iHeight); // height of animation
       
   123     
       
   124     // logical screen descriptor
       
   125     TUint8 packedFlags = 0;
       
   126 	packedFlags |= 8 - 1; // size of colour table is number of bits in each color table minus one (bits 0-2)
       
   127 	packedFlags |= (8 - 1) << 4; // colour resolution ie maximum size of the original colour palette (bits 4-6)
       
   128 	packedFlags |= 0x80; // use global colour table (bit 7)
       
   129     
       
   130     WriteInt8L(packedFlags);
       
   131     
       
   132     WriteInt8L(TRANSPARENCY_INDEX); // background color index
       
   133     
       
   134     WriteInt8L(0); // pixel aspect ratio, 0=not used
       
   135     
       
   136     // write the Symbian default palette since that's what is used
       
   137     CPalette* palette = CPalette::NewDefaultL(EColor256);
       
   138     CleanupStack::PushL(palette);
       
   139     
       
   140     for (TInt i=0; i<palette->Entries(); i++)
       
   141         {
       
   142         TRgb entry = palette->GetEntry(i);
       
   143 
       
   144         WriteInt8L(entry.Red());
       
   145         WriteInt8L(entry.Green());
       
   146         WriteInt8L(entry.Blue());
       
   147         }
       
   148     
       
   149     CleanupStack::PopAndDestroy(); //palette
       
   150     }
       
   151 
       
   152 // ---------------------------------------------------------------------------
       
   153 
       
   154 void CGifAnimator::WriteGraphicControlL(const TVideoFrame& aFrame)
       
   155     {
       
   156     TInt packedFlags(0);
       
   157     
       
   158     // enable transparency if needed
       
   159     if (aFrame.iEnableTransparency)
       
   160         packedFlags |= 0x01; 
       
   161 
       
   162     // set disposal method:
       
   163     // 0 = disposal method not specified, 1 = do not dispose of graphic,
       
   164     // 2 = overwrite graphic with background color, 3 = overwrite graphic with previous graphic
       
   165     TInt disposalMethod = 1;
       
   166     packedFlags |= ((disposalMethod << 2) & 0x1c);
       
   167     
       
   168     WriteInt8L(0x21); // GIF extension
       
   169     WriteInt8L(0xf9); // GIF graphic control block
       
   170     WriteInt8L(0x04); // block size
       
   171     WriteInt8L(packedFlags); // packed
       
   172     WriteInt16L(aFrame.iDelay); // delay
       
   173     WriteInt8L(TRANSPARENCY_INDEX); // transparent color index
       
   174     WriteInt8L(0); // block terminator, always 0
       
   175     }
       
   176 
       
   177 // ---------------------------------------------------------------------------
       
   178 
       
   179 void CGifAnimator::WriteImageDescriptorL(const TVideoFrame& aFrame)
       
   180     {
       
   181     WriteInt8L(0x2c); // GIF image descriptor
       
   182     WriteInt16L(aFrame.iXPos);
       
   183     WriteInt16L(aFrame.iYPos);
       
   184     WriteInt16L(aFrame.iWidth);
       
   185     WriteInt16L(aFrame.iHeight);
       
   186     WriteInt8L(0); // packed flags, none specified in this case
       
   187     }
       
   188     
       
   189 // ---------------------------------------------------------------------------
       
   190 
       
   191 CFbsBitmap* CGifAnimator::GetBitmapLC(TVideoFrame& aFrame, const TSize& aDimensions)
       
   192     {
       
   193     CFbsBitmap* bitmap = new(ELeave) CFbsBitmap;
       
   194     CleanupStack::PushL(bitmap);
       
   195     
       
   196     // read the bitmap from the temporary file
       
   197     RFile bitmapFile;
       
   198     User::LeaveIfError( bitmapFile.Open(iFs, aFrame.iFileStorePath, EFileRead) );
       
   199     RFileReadStream readStream(bitmapFile);
       
   200     bitmap->InternalizeL(readStream);
       
   201     readStream.Close();
       
   202     bitmapFile.Close();
       
   203 
       
   204     // delete the temporary file since it's not needed anymore
       
   205     iFs.Delete(aFrame.iFileStorePath);
       
   206     
       
   207     // resize the bitmap to match the video dimensions if it is a full screen one
       
   208     if (aFrame.iFillsWholeScreen && (aFrame.iWidth != aDimensions.iWidth || aFrame.iHeight != aDimensions.iHeight))
       
   209         {
       
   210         if (bitmap->Resize(aDimensions) == KErrNone)
       
   211             {
       
   212             // also update dimensions of this frame to match the dimensions of the video            
       
   213             aFrame.iWidth = aDimensions.iWidth;
       
   214             aFrame.iHeight = aDimensions.iHeight;
       
   215             }
       
   216         }
       
   217     
       
   218     return bitmap;
       
   219     }
       
   220     
       
   221 // ---------------------------------------------------------------------------
       
   222 
       
   223 void CGifAnimator::WriteRasterDataL(CFbsBitmap* aBitmap)
       
   224     {
       
   225     // reset the encoder
       
   226     if (iImageEncoder)
       
   227         {
       
   228 	    delete iImageEncoder;
       
   229         iImageEncoder = NULL;
       
   230         }
       
   231     
       
   232     // make sure the buffer for conversion is empty    
       
   233     if (iGIFImageData)
       
   234         {
       
   235 	    delete iGIFImageData;
       
   236         iGIFImageData = NULL;
       
   237         }        
       
   238         
       
   239     // init & convert the bitmap to GIF format
       
   240     iImageEncoder = CImageEncoder::DataNewL(iGIFImageData, CImageEncoder::EOptionNone, KImageTypeGIFUid);
       
   241     iImageEncoder->Convert( &iStatus, *aBitmap );
       
   242 
       
   243     // execute the async operation and wait till it's finished    
       
   244     SetActive();
       
   245     iWait.Start();
       
   246     
       
   247     // check any erros in active object
       
   248     User::LeaveIfError( iStatus.Int() );
       
   249     
       
   250     // check if we have valid data
       
   251     if (iGIFImageData == NULL || iGIFImageData->Des().Length()<793)
       
   252         User::Leave(KErrNoMemory);
       
   253     
       
   254     // in GIF files generated by Symbian, the raster data always starts at offset 791,
       
   255     // initial code size in GIF encoding should be 8 since we have a 8bpp image,
       
   256     // also check that the second last byte is the terminator 0,
       
   257     // if this check fails in newer releases of S60, proper parsing of GIF format is probably needed
       
   258     TUint8* imagePtr = &iGIFImageData->Des()[0];
       
   259     if (imagePtr[791] != 8 || imagePtr[iGIFImageData->Des().Length()-2] != 0)
       
   260         User::Leave(KErrNotSupported);
       
   261     
       
   262     // write the raster data block to the file
       
   263     TUint writeLength = iGIFImageData->Des().Length() - 1 - 791;
       
   264     imagePtr+=791;
       
   265     TPtr8 writePtr(imagePtr, writeLength, writeLength);
       
   266     User::LeaveIfError( iOutFile.Write(writePtr) );
       
   267     }
       
   268 
       
   269 // ---------------------------------------------------------------------------
       
   270 
       
   271 void CGifAnimator::WriteFooterL()
       
   272     {
       
   273     WriteInt8L(0x3b); // GIF trailer
       
   274     }
       
   275     
       
   276 // ---------------------------------------------------------------------------
       
   277 
       
   278 void CGifAnimator::FinishL()
       
   279     {
       
   280     iOutFile.Close();
       
   281     iFs.Close();
       
   282     }
       
   283 
       
   284 // ---------------------------------------------------------------------------
       
   285 
       
   286 void CGifAnimator::WriteInt8L(TInt aValue)
       
   287     {
       
   288     HBufC8* buf = HBufC8::NewMaxLC(1);
       
   289 
       
   290     TUint8* ptr = &buf->Des()[0];
       
   291     ptr[0] = TUint8(aValue);
       
   292     
       
   293     User::LeaveIfError( iOutFile.Write(buf->Des()) );
       
   294     
       
   295     CleanupStack::PopAndDestroy(); // buf
       
   296     }
       
   297 
       
   298 // ---------------------------------------------------------------------------
       
   299 
       
   300 void CGifAnimator::WriteInt16L(TInt aValue)
       
   301     {
       
   302     HBufC8* buf = HBufC8::NewMaxLC(2);
       
   303 
       
   304     TUint8* ptr = &buf->Des()[0];
       
   305     ptr[0] = TUint8(aValue);
       
   306     ptr[1] = TUint8(aValue>>8);
       
   307         
       
   308     User::LeaveIfError( iOutFile.Write(buf->Des()) );
       
   309     
       
   310     CleanupStack::PopAndDestroy(); // buf
       
   311     }
       
   312 
       
   313 // ---------------------------------------------------------------------------
       
   314 
       
   315 void CGifAnimator::DoCancel()
       
   316     {
       
   317     CAknEnv::StopSchedulerWaitWithBusyMessage( iWait );
       
   318     }
       
   319 
       
   320 // ---------------------------------------------------------------------------
       
   321 
       
   322 void CGifAnimator::RunL()
       
   323     {
       
   324     CAknEnv::StopSchedulerWaitWithBusyMessage( iWait );
       
   325     }
       
   326 
       
   327 // ---------------------------------------------------------------------------
       
   328 
       
   329 void CGifAnimator::DialogDismissedL(TInt aButtonId)
       
   330     {
       
   331     // check if cancel button was pressed
       
   332     if (aButtonId == EAknSoftkeyCancel)
       
   333         {
       
   334         }
       
   335     }
       
   336 
       
   337 // ---------------------------------------------------------------------------
       
   338 
       
   339 /*************************************************************************************************/
       
   340 // a helper class to display the progress dialog on the screen
       
   341 
       
   342 CSavingProgressDialog::CSavingProgressDialog() : CActive(EPriorityLow)
       
   343     {
       
   344 	CActiveScheduler::Add(this);
       
   345     }
       
   346 
       
   347 void CSavingProgressDialog::ConstructL(MDialogCallback* aDialogCallback)
       
   348     {
       
   349     iGlobalProgressDialog = CAknGlobalProgressDialog::NewL();
       
   350     iDialogCallback = aDialogCallback;
       
   351     }
       
   352 
       
   353 CSavingProgressDialog* CSavingProgressDialog::NewL(MDialogCallback* aDialogCallback)
       
   354     {
       
   355     CSavingProgressDialog* self = new(ELeave) CSavingProgressDialog();
       
   356     CleanupStack::PushL(self);
       
   357     self->ConstructL(aDialogCallback);
       
   358     CleanupStack::Pop(self);
       
   359     return self;
       
   360     }
       
   361 
       
   362 CSavingProgressDialog::~CSavingProgressDialog()
       
   363     {
       
   364     Cancel();
       
   365     delete iGlobalProgressDialog;
       
   366     }
       
   367 
       
   368 void CSavingProgressDialog::StartDisplayingL(const TDesC &aText, TInt aFinalValue)
       
   369     {
       
   370     if (!iVisible)
       
   371         {
       
   372         iFinalValue = aFinalValue;
       
   373         iCurrentValue = 0;
       
   374         
       
   375         iGlobalProgressDialog->ShowProgressDialogL(iStatus, aText, R_AVKON_SOFTKEYS_EMPTY,
       
   376             aFinalValue, CAknNoteDialog::ENoTone);
       
   377             
       
   378         iVisible = ETrue;
       
   379         SetActive();
       
   380         }
       
   381     }
       
   382 
       
   383 void CSavingProgressDialog::DoCancel()
       
   384     {
       
   385     if (iGlobalProgressDialog)
       
   386         {
       
   387         iGlobalProgressDialog->CancelProgressDialog();
       
   388         }
       
   389     iVisible = EFalse;
       
   390     }
       
   391 
       
   392 void CSavingProgressDialog::RunL()
       
   393     {
       
   394     if (iGlobalProgressDialog && iStatus.Int() == KErrNotFound)
       
   395         {
       
   396         // user dismissed the dialog
       
   397         iDialogCallback->DialogDismissedL(EAknSoftkeyCancel);
       
   398         }
       
   399     }
       
   400 
       
   401 void CSavingProgressDialog::IncreaseProgressValueWithOne()
       
   402     {
       
   403     iCurrentValue++;
       
   404     iGlobalProgressDialog->UpdateProgressDialog(iCurrentValue, iFinalValue);
       
   405     }
       
   406 
       
   407 void CSavingProgressDialog::ProcessFinished()
       
   408     {
       
   409     iGlobalProgressDialog->ProcessFinished();
       
   410     iVisible = EFalse;
       
   411     }
       
   412 
       
   413 /*************************************************************************************************/