examples/Multimedia/ICL/ICLCodec/PNGCodec.cpp

00001 // PngCodec.CPP
00002 //
00003 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
00004 // All rights reserved.
00005 // This component and the accompanying materials are made available
00006 // under the terms of "Eclipse Public License v1.0"
00007 // which accompanies this distribution, and is available
00008 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
00009 //
00010 // Initial Contributors:
00011 // Nokia Corporation - initial contribution.
00012 //
00013 // Contributors:
00014 //
00015 // Description:
00016 //
00017 //
00018 
00019 #include <fbs.h>
00020 
00021 #include "ImageUtils.h"
00022 #include "PNGCodec.h"
00023 
00024 // Constants.
00025 const TInt KTwipsPerMeter = 56693;
00026 
00027 // 
00028 // TPngImageInformation: PNG image information
00029 //
00030 
00031 // Initialise default PNG image information
00032 TPngImageInformation::TPngImageInformation()
00033         {
00034         iSize.SetSize(0,0);
00035         iBitDepth = 0;
00036         iColorType = EGrayscale;
00037         iCompressionMethod = EDeflateInflate32K;
00038         iFilterMethod = EAdaptiveFiltering;
00039         iInterlaceMethod = ENoInterlace;
00040         iPalettePresent = EFalse;
00041 
00042 #if defined(_DEBUG)
00043         // as an optimisation, we are going to set iPalette to all zeros, instead of setting
00044         // each element to KRgbBlack. This assumes that KRbgBlack is itself zero.
00045         ASSERT(sizeof(TRgb)==sizeof(TUint32)); // ie no new fields
00046         ASSERT(KRgbBlack.Value()==0); // ie the one value is zero
00047 #endif // defined(_DEBUG)
00048 
00049         Mem::FillZ(iPalette, KPngMaxPLTESize*sizeof(TRgb));
00050 
00051         iBackgroundPresent = EFalse;
00052         iBackgroundColor = KRgbWhite;
00053         iPhysicalPresent = EFalse;
00054         iPhysicalUnits = EUnknownUnits;
00055         iPhysicalSize.SetSize(0,0);
00056         iTransparencyPresent = EFalse;
00057         Mem::Fill(iTransparencyValue,KPngMaxPLTESize,0xff);
00058         }
00059 
00060 
00061 //
00062 // CPngReadCodec: reads a PNG image
00063 //
00064 
00065 CPngReadCodec::~CPngReadCodec()
00066         {
00067         delete iDecoder;
00068         delete iDecompressor;
00069         }
00070 
00071 // Called by framework when a Convert operation begins
00072 void CPngReadCodec::InitFrameL(TFrameInfo& /*aFrameInfo*/, CFrameImageData& /*aFrameImageData*/, 
00073         TBool aDisableErrorDiffusion, CFbsBitmap& aDestination, CFbsBitmap* aDestinationMask)
00074         {
00075         CFbsBitmap& newFrame = aDestination;
00076 
00077         TPoint& pos = Pos();
00078         pos.SetXY(0,0);
00079         iChunkBytesRemaining = 0;
00080 
00081         // Use the supplied image processor
00082         CImageProcessor* imageProc = ImageProcessorUtility::NewImageProcessorL(newFrame, iImageInfo.iSize, ERgb, aDisableErrorDiffusion);
00083         SetImageProcessor(imageProc);
00084         imageProc->PrepareL(newFrame,iImageInfo.iSize);
00085 
00086         CImageProcessor* maskProc = NULL;
00087         SetMaskProcessor(NULL);
00088 
00089         // If transparency is being used, create a bitmap mask as well
00090         if ((iImageInfo.iTransparencyPresent || (iImageInfo.iColorType & TPngImageInformation::EAlphaChannelUsed))
00091                 && aDestinationMask)
00092                 {
00093                 maskProc = ImageProcessorUtility::NewImageProcessorL(*aDestinationMask, iImageInfo.iSize, ERgb, aDisableErrorDiffusion);
00094                 SetMaskProcessor(maskProc);
00095                 maskProc->PrepareL(*aDestinationMask,iImageInfo.iSize);
00096                 // set mask to black so that unknown parts on streamed image are not drawn
00097                 ClearBitmapL(*aDestinationMask, KRgbBlack);
00098                 }
00099 
00100         // Create a helper to read the scan lines
00101         delete iDecoder;
00102         iDecoder = NULL;
00103         iDecoder = CPngReadSubCodec::NewL(imageProc,maskProc,iImageInfo);
00104 
00105         // And a unzipper to decompress image data
00106         if (!iDecompressor)
00107                 iDecompressor = CEZDecompressor::NewL(*this);
00108         else
00109                 iDecompressor->ResetL(*this);
00110 
00111         if (maskProc==NULL)
00112                 {
00113                 // if no mask, clear destination for sensible behaviour on streamed partial images
00114                 TRgb background = iImageInfo.iBackgroundPresent ? iImageInfo.iBackgroundColor : KRgbWhite;
00115                 ClearBitmapL(aDestination, background);
00116                 }
00117         }
00118 
00119 // Called by framework to initialise image frame header
00120 void CPngReadCodec::InitFrameHeader(TFrameInfo& aFrameSettings, CFrameImageData& /* aFrameImageData */)
00121         {
00122         ASSERT(aFrameSettings.CurrentFrameState() == TFrameInfo::EFrameInfoUninitialised);
00123         iFrameInfo = &aFrameSettings;
00124         iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrameHeader);
00125         }
00126 
00127 // Called by framework to process a header for a frame
00128 TFrameState CPngReadCodec::ProcessFrameHeaderL(TBufPtr8& aData)
00129         {
00130         const TUint8* startDataPtr = aData.Ptr();
00131         const TUint8* dataPtr = startDataPtr;
00132         const TUint8* dataPtrLimit = startDataPtr + aData.Length();
00133 
00134         // Process the mandatory PNG header chunk: sets up iImageInfo
00135         if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrameHeader)
00136                 {
00137                 if (dataPtr + KPngChunkLengthSize + KPngChunkIdSize + KPngIHDRChunkSize + KPngChunkCRCSize > dataPtrLimit)
00138                         User::Leave(KErrUnderflow);
00139 
00140                 TInt chunkLength = PtrReadUtil::ReadBigEndianUint32Inc(dataPtr);
00141                 TPtrC8 chunkId(dataPtr,KPngChunkIdSize);
00142 
00143                 if (chunkLength != KPngIHDRChunkSize || chunkId != KPngIHDRChunkId)
00144                         User::Leave(KErrNotFound);
00145 
00146                 dataPtr += KPngChunkIdSize;
00147 
00148                 DoProcessIHDRL(dataPtr,chunkLength);
00149 
00150                 dataPtr += KPngIHDRChunkSize + KPngChunkCRCSize;
00151                 }
00152 
00153         // Process any optional PNG header chunks
00154         TRAPD(err, DoProcessInfoL(dataPtr, dataPtrLimit));
00155         if (err != KErrNone)
00156                 {
00157                 if (err == KErrNotFound)
00158                         return EFrameComplete;
00159 
00160                 User::Leave(err); // A real error occured
00161                 }
00162 
00163         // Having read the header, can initialise the frame information
00164         aData.Shift(dataPtr - startDataPtr);
00165 
00166         iFrameInfo->iFrameCoordsInPixels.SetRect(TPoint(0,0),iImageInfo.iSize);
00167         iFrameInfo->iOverallSizeInPixels = iImageInfo.iSize;
00168         if (iImageInfo.iPhysicalPresent && iImageInfo.iPhysicalUnits == TPngImageInformation::EMeters)
00169                 iFrameInfo->iFrameSizeInTwips = iImageInfo.iPhysicalSize;
00170         else
00171                 iFrameInfo->iFrameSizeInTwips.SetSize(0,0);
00172 
00173         iFrameInfo->iBitsPerPixel = iImageInfo.iBitDepth;
00174         if (iImageInfo.iColorType & TPngImageInformation::EColorUsed)
00175                 iFrameInfo->iBitsPerPixel *= 3;
00176         
00177         iFrameInfo->iDelay = 0;
00178         iFrameInfo->iFlags = TFrameInfo::ECanDither;
00179         
00180         if (iImageInfo.iColorType & (TPngImageInformation::EPaletteUsed | TPngImageInformation::EColorUsed))
00181                 iFrameInfo->iFlags |= TFrameInfo::EColor;
00182         
00183         if (iImageInfo.iColorType & TPngImageInformation::EAlphaChannelUsed)
00184                 {
00185                 iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;
00186                 iFrameInfo->iFlags |= TFrameInfo::EAlphaChannel;
00187                 }
00188         else if (iImageInfo.iTransparencyPresent)
00189                 iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;
00190 
00191         switch (iFrameInfo->iBitsPerPixel)
00192                 {
00193         case 1:
00194                 iFrameInfo->iFrameDisplayMode = EGray2;
00195                 break;
00196 
00197         case 2:
00198                 iFrameInfo->iFrameDisplayMode = EGray4;
00199                 break;
00200 
00201         case 4:
00202                 iFrameInfo->iFrameDisplayMode = (iFrameInfo->iFlags & TFrameInfo::EColor) ? EColor16 : EGray16;
00203                 break;
00204 
00205         case 8:
00206                 iFrameInfo->iFrameDisplayMode = (iFrameInfo->iFlags & TFrameInfo::EColor) ? EColor256 : EGray256;
00207                 break;
00208 
00209         case 12:
00210                 iFrameInfo->iFrameDisplayMode = EColor4K;
00211                 break;
00212 
00213         case 16:
00214                 iFrameInfo->iFrameDisplayMode = EColor64K;
00215                 break;
00216 
00217         case 24:
00218                 iFrameInfo->iFrameDisplayMode = EColor16M;
00219                 break;
00220 
00221         default:
00222                 User::Leave(KErrCorrupt);
00223                 }
00224 
00225         if (iImageInfo.iBackgroundPresent)
00226                 iFrameInfo->iBackgroundColor = iImageInfo.iBackgroundColor;
00227 
00228         iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete);
00229         return EFrameComplete;
00230         }
00231 
00232 // Called by the framework to process frame data
00233 TFrameState CPngReadCodec::ProcessFrameL(TBufPtr8& aSrc)
00234         {
00235         CImageProcessor*const imageProc = ImageProcessor();
00236         CImageProcessor*const maskProc = MaskProcessor();
00237 
00238         TUint8* startDataPtr = const_cast<TUint8*>(aSrc.Ptr());
00239         TUint8* dataPtr = startDataPtr;
00240         const TUint8* dataPtrLimit = dataPtr + aSrc.Length();
00241         while (dataPtr < dataPtrLimit)
00242                 {
00243                 // If at the end of a PNG chunk
00244                 if (iChunkBytesRemaining == 0)
00245                         {
00246                         if (iChunkId == KPngIDATChunkId) // Need to skip IDAT chunk CRCs
00247                                 {
00248                                 if (dataPtr + KPngChunkCRCSize + KPngChunkLengthSize + KPngChunkIdSize > dataPtrLimit)
00249                                         break;
00250 
00251                                 dataPtr += KPngChunkCRCSize;
00252                                 }
00253                         else
00254                                 {
00255                                 if (dataPtr + KPngChunkLengthSize + KPngChunkIdSize > dataPtrLimit)
00256                                         break;
00257                                 }
00258 
00259                         iChunkBytesRemaining = PtrReadUtil::ReadBigEndianUint32Inc(const_cast<const TUint8*&>(dataPtr));
00260                         iChunkId = TPtr8(dataPtr,KPngChunkIdSize,KPngChunkIdSize);
00261                         dataPtr += KPngChunkIdSize;
00262                         }
00263                 // Process an image data chunk
00264                 if (iChunkId == KPngIDATChunkId)
00265                         DoProcessDataL(const_cast<const TUint8*&>(dataPtr),dataPtrLimit);
00266                 // Process an END chunk -- frame is complete
00267                 else if (iChunkId == KPngIENDChunkId)
00268                         {
00269                         iDecompressor->InflateL();
00270                         imageProc->FlushPixels();
00271                         if (maskProc)
00272                                 maskProc->FlushPixels();
00273                         return EFrameComplete;
00274                         }
00275                 else 
00276                 // Skip other chunks
00277                         {
00278                         TInt bytesLeft = dataPtrLimit - dataPtr;
00279                         if (bytesLeft >= iChunkBytesRemaining + KPngChunkCRCSize)
00280                                 {
00281                                 dataPtr += iChunkBytesRemaining + KPngChunkCRCSize;
00282                                 iChunkBytesRemaining = 0;
00283                                 }
00284                         else
00285                                 {
00286                                 dataPtr += bytesLeft;
00287                                 iChunkBytesRemaining -= bytesLeft;
00288                                 }
00289                         }
00290                 }
00291 
00292         aSrc.Shift(dataPtr - startDataPtr);
00293         return EFrameIncomplete;
00294         }
00295 
00296 // Process any optional PNG header chunks
00297 void CPngReadCodec::DoProcessInfoL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
00298         {
00299         FOREVER
00300                 {
00301                 if (aDataPtr + KPngChunkLengthSize + KPngChunkIdSize > aDataPtrLimit) // Check there is enough data to read the chunk length
00302                         User::Leave(KErrUnderflow);
00303 
00304                 TInt chunkLength = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
00305                 TPtrC8 chunkId (&aDataPtr[0],KPngChunkIdSize);
00306 
00307                 if (chunkId == KPngIDATChunkId)
00308                         {
00309                         aDataPtr -= KPngChunkLengthSize; // Rewind to start of chunkLength
00310                         break;
00311                         }
00312 
00313                 if (aDataPtr + KPngChunkIdSize + chunkLength + KPngChunkCRCSize > aDataPtrLimit) // Check there is enough data to read the whole chunk
00314                         {
00315                         aDataPtr -= KPngChunkLengthSize; // Rewind to start of chunkLength
00316                         User::Leave(KErrUnderflow);
00317                         }
00318 
00319                 aDataPtr += KPngChunkIdSize;
00320 
00321                 if (chunkId == KPngPLTEChunkId)
00322                         DoProcessPLTEL(aDataPtr,chunkLength);
00323                 else if (chunkId == KPngbKGDChunkId)
00324                         DoProcessbKGDL(aDataPtr,chunkLength);
00325                 else if (chunkId == KPngpHYsChunkId)
00326                         DoProcesspHYsL(aDataPtr,chunkLength);
00327                 else if (chunkId == KPngtRNSChunkId)
00328                         DoProcesstRNSL(aDataPtr,chunkLength);
00329                 else if (chunkId == KPngIHDRChunkId || chunkId == KPngIENDChunkId)
00330                         User::Leave(KErrCorrupt);
00331 
00332                 aDataPtr += chunkLength;
00333                 PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr); // Skip crc value
00334                 }
00335         }
00336 
00337 // Process the mandatory PNG header chunk
00338 void CPngReadCodec::DoProcessIHDRL(const TUint8* aDataPtr,TInt aChunkLength)
00339         {
00340         if (aChunkLength != KPngIHDRChunkSize)
00341                 User::Leave(KErrCorrupt);
00342 
00343         iImageInfo.iSize.iWidth = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
00344         iImageInfo.iSize.iHeight = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
00345         iImageInfo.iBitDepth = aDataPtr[0];
00346         iImageInfo.iColorType = TPngImageInformation::TColorType(aDataPtr[1]);
00347         iImageInfo.iCompressionMethod = TPngImageInformation::TCompressionMethod(aDataPtr[2]);
00348         iImageInfo.iFilterMethod = TPngImageInformation::TFilterMethod(aDataPtr[3]);
00349         iImageInfo.iInterlaceMethod = TPngImageInformation::TInterlaceMethod(aDataPtr[4]);
00350 
00351         // Check is one of the PNG formats we support
00352         if (iImageInfo.iSize.iWidth < 1 || iImageInfo.iSize.iHeight < 1
00353                 || iImageInfo.iCompressionMethod != TPngImageInformation::EDeflateInflate32K
00354                 || iImageInfo.iFilterMethod != TPngImageInformation::EAdaptiveFiltering
00355                 || (iImageInfo.iInterlaceMethod != TPngImageInformation::ENoInterlace &&
00356                         iImageInfo.iInterlaceMethod != TPngImageInformation::EAdam7Interlace))
00357                 User::Leave(KErrCorrupt);
00358         }
00359 
00360 // Process a PNG PLTE (palette) chunk
00361 void CPngReadCodec::DoProcessPLTEL(const TUint8* aDataPtr,TInt aChunkLength)
00362         {
00363         if (aChunkLength % 3 != 0)
00364                 User::Leave(KErrCorrupt);
00365 
00366         iImageInfo.iPalettePresent = ETrue;
00367 
00368         const TUint8* dataPtrLimit = aDataPtr + aChunkLength;
00369         TRgb* palettePtr = iImageInfo.iPalette;
00370 
00371         while (aDataPtr < dataPtrLimit)
00372                 {
00373                 *palettePtr++ = TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]);
00374                 aDataPtr += 3;
00375                 }
00376         }
00377 
00378 // Process a PNG bKGD (background color) chunk
00379 void CPngReadCodec::DoProcessbKGDL(const TUint8* aDataPtr,TInt aChunkLength)
00380         {
00381         iImageInfo.iBackgroundPresent = ETrue;
00382 
00383         if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor) // 3
00384                 {
00385                 if (aChunkLength < 1)
00386                         User::Leave(KErrCorrupt);
00387 
00388                 iImageInfo.iBackgroundColor = iImageInfo.iPalette[aDataPtr[0]];
00389                 }
00390         else if (iImageInfo.iColorType & TPngImageInformation::EMonochrome) // 0 & 4
00391                 {
00392                 if (aChunkLength < 2)
00393                         User::Leave(KErrCorrupt);
00394 
00395                 TInt grayLevel = PtrReadUtil::ReadBigEndianInt16(aDataPtr);
00396                 ASSERT(iImageInfo.iBitDepth<8);
00397                 grayLevel <<= (7-iImageInfo.iBitDepth);
00398                 iImageInfo.iBackgroundColor = TRgb::Gray256(grayLevel);
00399                 }
00400         else if (iImageInfo.iColorType & TPngImageInformation::EColorUsed) // 2 & 6
00401                 {
00402                 if (aChunkLength < 6)
00403                         User::Leave(KErrCorrupt);
00404 
00405                 TInt red = PtrReadUtil::ReadBigEndianInt16(&aDataPtr[0]);
00406                 TInt green = PtrReadUtil::ReadBigEndianInt16(&aDataPtr[2]);
00407                 TInt blue = PtrReadUtil::ReadBigEndianInt16(&aDataPtr[4]);
00408                 ASSERT (iImageInfo.iBitDepth<8);
00409                 const TInt offset = 7-iImageInfo.iBitDepth;
00410                 red <<= offset;
00411                 green <<= offset;
00412                 blue <<= offset;
00413                 iImageInfo.iBackgroundColor = TRgb(red,green,blue);
00414                 }
00415         }
00416 
00417 // Process a PNG pHYs (Physical pixel dimensions) chunk
00418 void CPngReadCodec::DoProcesspHYsL(const TUint8* aDataPtr,TInt aChunkLength)
00419         {
00420         if (aChunkLength != KPngpHYsChunkSize)
00421                 User::Leave(KErrCorrupt);
00422 
00423         iImageInfo.iPhysicalUnits = TPngImageInformation::TPhysicalUnits(aDataPtr[8]);
00424 
00425         if (iImageInfo.iPhysicalUnits == TPngImageInformation::EMeters)
00426                 {
00427                 iImageInfo.iPhysicalPresent = ETrue;
00428 
00429                 TInt horzPixelsPerMeter = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
00430                 TInt vertPixelsPerMeter = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
00431 
00432                 if (horzPixelsPerMeter > 0)
00433                         iImageInfo.iPhysicalSize.iWidth = iImageInfo.iSize.iWidth * KTwipsPerMeter / horzPixelsPerMeter;
00434                 if (vertPixelsPerMeter > 0)
00435                         iImageInfo.iPhysicalSize.iHeight = iImageInfo.iSize.iHeight * KTwipsPerMeter / vertPixelsPerMeter;
00436                 }
00437         }
00438 
00439 // Process a PNG tRNS (Transparency) chunk
00440 void CPngReadCodec::DoProcesstRNSL(const TUint8* aDataPtr,TInt aChunkLength)
00441         {
00442         iImageInfo.iTransparencyPresent = ETrue;
00443 
00444         if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor) // 3
00445                 {
00446                 if (aChunkLength < 1)
00447                         User::Leave(KErrCorrupt);
00448 
00449                 Mem::Copy(iImageInfo.iTransparencyValue,aDataPtr,aChunkLength);
00450                 }
00451         else if (iImageInfo.iColorType == TPngImageInformation::EGrayscale) // 0
00452                 {
00453                 if (aChunkLength < 2)
00454                         User::Leave(KErrCorrupt);
00455 
00456                 iImageInfo.iTransparentGray = TUint16((aDataPtr[0] << 8) | aDataPtr[1]);
00457                 }
00458         else if (iImageInfo.iColorType == TPngImageInformation::EDirectColor) // 2
00459                 {
00460                 if (aChunkLength < 6)
00461                         User::Leave(KErrCorrupt);
00462 
00463                 iImageInfo.iTransparentRed = TUint16((aDataPtr[0] << 8) | aDataPtr[1]);
00464                 iImageInfo.iTransparentGreen = TUint16((aDataPtr[2] << 8) | aDataPtr[3]);
00465                 iImageInfo.iTransparentBlue = TUint16((aDataPtr[4] << 8) | aDataPtr[5]);
00466                 }
00467         }
00468 
00469 // Process a PNG image data
00470 void CPngReadCodec::DoProcessDataL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
00471         {
00472         // Data is passed to the decompressor
00473         TInt bytesToProcess = Min(aDataPtrLimit - aDataPtr,iChunkBytesRemaining);
00474         iDataDes.Set(aDataPtr,bytesToProcess);
00475         iDecompressor->SetInput(iDataDes);
00476 
00477         while (iDecompressor->AvailIn() > 0)
00478                 iDecompressor->InflateL();
00479 
00480         aDataPtr += bytesToProcess;
00481         iChunkBytesRemaining -= bytesToProcess;
00482         }
00483 
00484 // From MEZBufferManager: manage decompressor stream
00485 void CPngReadCodec::InitializeL(CEZZStream& aZStream)
00486         {
00487         aZStream.SetOutput(iDecoder->FirstBuffer());
00488         }
00489 
00490 void CPngReadCodec::NeedInputL(CEZZStream& /*aZStream*/)
00491         {
00492         }
00493 
00494 void CPngReadCodec::NeedOutputL(CEZZStream& aZStream)
00495         {
00496         aZStream.SetOutput(iDecoder->DecodeL());
00497         }
00498 
00499 void CPngReadCodec::FinalizeL(CEZZStream& /*aZStream*/)
00500         {
00501         iDecoder->DecodeL();
00502         }
00503 
00504 
00505 
00506 //
00507 // CPngWriteCodec: writes a PNG image
00508 //
00509 
00510 
00511 CPngWriteCodec::CPngWriteCodec(TInt aBpp, TBool aColor, TBool aPaletted, TInt aCompressionLevel)
00512         : iCompressionLevel(aCompressionLevel), iCompressorPtr(NULL, 0)
00513         {
00514         // Set bpp
00515         iImageInfo.iBitsPerPixel = aBpp;
00516         switch (aBpp)
00517                 {
00518                 case 1:
00519                         iImageInfo.iBitDepth = 1;
00520                         break;
00521                 case 2:
00522                         iImageInfo.iBitDepth = 2;
00523                         break;
00524                 case 4:
00525                         iImageInfo.iBitDepth = 4;
00526                         break;
00527                 case 8:
00528                 case 24:
00529                         iImageInfo.iBitDepth = 8;
00530                         break;
00531                 default:
00532                         break;
00533                 }
00534 
00535         // Set color type
00536         if (aColor && aPaletted)
00537                 iImageInfo.iColorType = TPngImageInformation::EIndexedColor;
00538         else if (aColor)
00539                 iImageInfo.iColorType = TPngImageInformation::EDirectColor;
00540         else
00541                 iImageInfo.iColorType = TPngImageInformation::EGrayscale;
00542         }
00543 
00544 CPngWriteCodec::~CPngWriteCodec()
00545         {
00546         delete iCompressor;
00547         delete iEncoder;
00548         }
00549 
00550 // Called by framework at start of conversion operation
00551 void CPngWriteCodec::InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource)
00552         {
00553         if (aDst.Length() == 0)
00554                 User::Leave(KErrArgument);      // Not enough length for anything
00555 
00556         SetSource(&aSource);
00557         iDestStartPtr = const_cast<TUint8*>(aDst.Ptr());
00558         iDestPtr = iDestStartPtr;
00559         iDestPtrLimit = iDestPtr + aDst.MaxLength();
00560 
00561         // Set image information
00562         const SEpocBitmapHeader& header = aSource.Header();
00563         iImageInfo.iSize = header.iSizeInPixels;
00564 
00565         switch (iImageInfo.iBitDepth)
00566                 {
00567                 case 1:
00568                 case 2:
00569                 case 4:
00570                         if (iImageInfo.iColorType == TPngImageInformation::EDirectColor)
00571                                 {
00572                                 // Bit depths 1, 2 and 4 don't support RGB colour (color mode 2)
00573                                 // Must use paletted colour or greyscale
00574                                 User::Leave(KErrNotSupported);
00575                                 break;
00576                                 }
00577                         // fall through to case 8
00578                 case 8:
00579                         break;
00580                 default:
00581                         User::Leave(KErrNotSupported);  // unsupported bit depth
00582                         break;
00583                 }
00584 
00585         iImageInfo.iCompressionMethod = TPngImageInformation::EDeflateInflate32K;
00586         iImageInfo.iFilterMethod = TPngImageInformation::EAdaptiveFiltering;
00587         iImageInfo.iInterlaceMethod = TPngImageInformation::ENoInterlace;
00588 
00589         // Create encoder
00590         if (iEncoder)
00591                 {
00592                 delete iEncoder;
00593                 iEncoder = NULL;
00594                 }
00595         iEncoder = CPngWriteSubCodec::NewL(iImageInfo, &aSource);
00596 
00597         // Create compressor
00598         if (iCompressor)
00599                 {
00600                 delete iCompressor;
00601                 iCompressor = NULL;
00602                 }
00603         iCompressor = CEZCompressor::NewL(*this, iCompressionLevel);
00604 
00605         // Initial encoder state
00606         if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor)
00607                 iEncoderState = EPngWritePLTE;  
00608         else
00609                 iEncoderState = EPngDeflate;
00610         iCallAgain = ETrue;             // to make sure we call DeflateL
00611 
00612         // Write header
00613         User::LeaveIfError(WriteHeaderChunk(aDst));
00614         }
00615 
00616 // Called by the framework to process frame data
00617 TFrameState CPngWriteCodec::ProcessFrameL(TBufPtr8& aDst)
00618         {
00619         if (aDst.Length() == 0)
00620                 User::Leave(KErrArgument);      // Not enough length for anything
00621 
00622         TFrameState state = EFrameIncomplete;
00623         iDestStartPtr = const_cast<TUint8*>(aDst.Ptr());
00624         iDestPtr = iDestStartPtr;
00625         iDestPtrLimit = iDestPtr + aDst.MaxLength();
00626 
00627         // Set return buffer length to 0 initially
00628         aDst.SetLength(0);
00629 
00630         while (aDst.Length() == 0 && state != EFrameComplete)
00631                 {
00632                 // Loop round until we have some data to return or
00633                 // the image is encoded
00634                 switch (iEncoderState)
00635                         {
00636                         case EPngWritePLTE:
00637                                 WritePLTEChunk(aDst);
00638                                 break;
00639                         case EPngDeflate:
00640                                 DeflateEncodedDataL(aDst, state);
00641                                 break;
00642                         case EPngWriteIDAT:
00643                                 WriteIDATChunk(aDst);
00644                                 break;
00645                         case EPngFlush:
00646                                 FlushCompressedDataL(aDst, state);
00647                                 break;
00648                         case EPngEndChunk:
00649                                 WriteEndChunk(aDst);
00650                                 state = EFrameComplete;
00651                                 break;
00652                         default:
00653                                 break;
00654                         }
00655                 }
00656 
00657         return state;
00658         }
00659 
00660 // Write a compressed image data chunk
00661 void CPngWriteCodec::DeflateEncodedDataL(TBufPtr8& aDst, TFrameState& /*aState*/)
00662         {
00663         // Set ptr for compressed data
00664         const TInt dataLength = aDst.MaxLength() - KPngChunkLengthSize - KPngChunkIdSize - KPngChunkCRCSize;
00665         ASSERT(dataLength > 0);
00666         iCompressorPtr.Set(iDestPtr + KPngChunkIdSize + KPngChunkLengthSize, dataLength, dataLength);
00667 
00668         // Initialise input/output for compressor
00669         iCompressor->SetInput(iEncoder->EncodeL(iScanline));
00670         iScanline++;
00671         iCompressor->SetOutput(iCompressorPtr);
00672 
00673         while ((iEncoderState == EPngDeflate) && iCallAgain)
00674                 iCallAgain = iCompressor->DeflateL();
00675 
00676         // Write the IDAT chunk
00677         WriteIDATChunk(aDst);
00678         iEncoderState = EPngFlush;
00679         }
00680 
00681 void CPngWriteCodec::FlushCompressedDataL(TBufPtr8& aDst, TFrameState& /*aState*/)
00682         {
00683         if (iCallAgain)
00684                 {
00685                 iCallAgain = iCompressor->DeflateL();
00686                 WriteIDATChunk(aDst);
00687                 }
00688         else
00689                 {
00690                 iEncoderState = EPngEndChunk;
00691                 }
00692         }
00693 
00694 // Write a PLTE chunk
00695 void CPngWriteCodec::WritePLTEChunk(TBufPtr8& aDst)
00696         {
00697         ASSERT(iEncoder->Palette() &&
00698                    (iImageInfo.iColorType == TPngImageInformation::EIndexedColor ||
00699                     iImageInfo.iColorType == TPngImageInformation::EDirectColor ||
00700                     iImageInfo.iColorType == TPngImageInformation::EAlphaDirectColor)); // allowed color types for PLTE chunk
00701 
00702         // Get palette entries
00703         CPalette* palette = iEncoder->Palette();
00704         ASSERT(palette);
00705         const TInt count = palette->Entries();
00706         TUint8* ptr = iDestPtr + KPngChunkIdSize + KPngChunkLengthSize;
00707         TInt length = count * 3;
00708         TPtr8 data(ptr, length, length);
00709         for (TInt i=0; i < count; i++)
00710                 {
00711                 TRgb rgb = palette->GetEntry(i);
00712                 *ptr = TUint8(rgb.Red());
00713                 ptr++;
00714                 *ptr = TUint8(rgb.Green());
00715                 ptr++;
00716                 *ptr = TUint8(rgb.Blue());
00717                 ptr++;
00718                 }
00719         // Write PLTE chunk
00720         WritePngChunk(iDestPtr, KPngPLTEChunkId, data, length);
00721         ASSERT(length % 3 == 0);        // length must be divisible by 3
00722         aDst.SetLength(length);
00723         iEncoderState = EPngDeflate;
00724         }
00725 
00726 // Write a data chunk
00727 void CPngWriteCodec::WriteIDATChunk(TBufPtr8& aDst)
00728         {
00729         TPtrC8 ptr(iCompressor->OutputDescriptor());
00730         if (ptr.Length())
00731                 {
00732                 TInt length = 0;
00733                 WritePngChunk(iDestPtr, KPngIDATChunkId, ptr, length);
00734                 aDst.SetLength(length);
00735 
00736                 // New output can write to the same compressor ptr
00737                 iCompressor->SetOutput(iCompressorPtr);
00738                 }
00739 
00740         if (iCallAgain)
00741                 iEncoderState = EPngFlush;
00742         }
00743 
00744 // Write an END chunk
00745 void CPngWriteCodec::WriteEndChunk(TBufPtr8& aDst)
00746         {
00747         // Write IEND chunk
00748         TInt length = 0;
00749         WritePngChunk(iDestPtr, KPngIENDChunkId, KNullDesC8, length);
00750         aDst.SetLength(length);
00751         }
00752 
00753 // Write a header chunk
00754 TInt CPngWriteCodec::WriteHeaderChunk(TBufPtr8& aDst)
00755         {
00756         // Write signature
00757         Mem::Copy(iDestPtr, &KPngSignature[0], KPngFileSignatureLength);
00758         iDestPtr += KPngFileSignatureLength;
00759 
00760         // Write IHDR chunk
00761         TBuf8<KPngIHDRChunkSize> buffer;
00762         TUint8* ptr = const_cast<TUint8*>(buffer.Ptr());
00763         // Set length of data
00764         buffer.SetLength(KPngIHDRChunkSize);
00765         // Chunk data
00766         // width (4 bytes)
00767         if ((iImageInfo.iSize.iWidth == 0) ||
00768                 (static_cast<TUint>(iImageInfo.iSize.iWidth) > KPngMaxImageSize))
00769                 {
00770                 return KErrArgument;    // invalid width
00771                 }
00772         PtrWriteUtil::WriteBigEndianInt32(ptr, iImageInfo.iSize.iWidth);
00773         ptr += 4;
00774         // height (4 bytes)
00775         if ((iImageInfo.iSize.iHeight == 0) ||
00776                 (static_cast<TUint>(iImageInfo.iSize.iHeight) > KPngMaxImageSize))
00777                 {
00778                 return KErrArgument;    // invalid height
00779                 }
00780         PtrWriteUtil::WriteBigEndianInt32(ptr, iImageInfo.iSize.iHeight);
00781         ptr += 4;
00782         // bit depth (1 byte)
00783         PtrWriteUtil::WriteInt8(ptr, iImageInfo.iBitDepth);
00784         ptr++;
00785         // colour type (1 byte)
00786         PtrWriteUtil::WriteInt8(ptr, iImageInfo.iColorType);
00787         ptr++;
00788         // compression method (1 byte)
00789         PtrWriteUtil::WriteInt8(ptr, iImageInfo.iCompressionMethod);
00790         ptr++;
00791         // filter method (1 byte)
00792         PtrWriteUtil::WriteInt8(ptr, iImageInfo.iFilterMethod);
00793         ptr++;
00794         // interlace method (1 byte)
00795         PtrWriteUtil::WriteInt8(ptr, iImageInfo.iInterlaceMethod);
00796         ptr++;
00797 
00798         TInt length = 0;
00799         WritePngChunk(iDestPtr, KPngIHDRChunkId, buffer, length);
00800         aDst.SetLength(KPngFileSignatureLength + length);
00801 
00802         return KErrNone;
00803         }
00804 
00805 // Chunk writing helper function
00806 void CPngWriteCodec::WritePngChunk(TUint8*& aDestPtr, const TDesC8& aChunkId, const TDesC8& aData, TInt& aLength)
00807         {
00808         // Chunk length (4 bytes)
00809         PtrWriteUtil::WriteBigEndianInt32(aDestPtr, aData.Length());
00810         aDestPtr += KPngChunkLengthSize;
00811         TUint8* crcPtr = aDestPtr;      // start position for calculating CRC
00812         // Chunk type (4 bytes)
00813         Mem::Copy(aDestPtr, aChunkId.Ptr(), KPngChunkIdSize);
00814         aDestPtr += KPngChunkIdSize;
00815         // Chunk data (0...n bytes)
00816         Mem::Copy(aDestPtr, aData.Ptr(), aData.Length());
00817         aDestPtr += aData.Length();
00818         // CRC (4 bytes)
00819         TUint32 crc = KPngCrcMask;
00820         GetCrc(crc, crcPtr, KPngChunkIdSize + aData.Length());
00821         crc ^= KPngCrcMask;
00822         PtrWriteUtil::WriteBigEndianInt32(aDestPtr, crc);
00823         aDestPtr += KPngChunkCRCSize;
00824         // Length of chunk
00825         aLength = KPngChunkLengthSize + KPngChunkIdSize + aData.Length() + KPngChunkCRCSize;
00826         }
00827 
00828 // from MEZBufferManager, manage data compressor
00829 void CPngWriteCodec::InitializeL(CEZZStream& /*aZStream*/)
00830         {
00831         }
00832 
00833 void CPngWriteCodec::NeedInputL(CEZZStream& aZStream)
00834         {
00835         // Give compressor more data from encoder
00836         aZStream.SetInput(iEncoder->EncodeL(iScanline));
00837         if (iCompressor->AvailIn() != 0)
00838                 iScanline++;
00839         }
00840 
00841 void CPngWriteCodec::NeedOutputL(CEZZStream& /*aZStream*/)
00842         {
00843         // Signal to write an IDAT chunk
00844         iEncoderState = EPngWriteIDAT;
00845         }
00846 
00847 void CPngWriteCodec::FinalizeL(CEZZStream& /*aZStream*/)
00848         {
00849         }
00850 
00851 // Calculate CRC for PNG chunks
00852 void CPngWriteCodec::GetCrc(TUint32& aCrc, const TUint8* aPtr, const TInt aLength)
00853         {
00854         if (!iCrcTableCalculated)
00855                 CalcCrcTable();
00856         TUint32 code = aCrc;
00857         for (TInt i=0; i < aLength; i++)
00858                 code = iCrcTable[(code ^ aPtr[i]) & 0xff] ^ (code >> 8);
00859         aCrc = code;
00860         }
00861 
00862 void CPngWriteCodec::CalcCrcTable()
00863         {
00864         for (TInt i=0; i < KPngCrcTableLength; i++)
00865                 {
00866                 TUint32 code = static_cast<TUint32>(i);
00867 
00868                 for (TInt j = 0; j < 8; j++)
00869                         {
00870                         if (code & 1)
00871                                 code = 0xedb88320 ^ (code >> 1);
00872                         else
00873                                 code = code >> 1;
00874                         }
00875                 iCrcTable[i] = code;
00876                 }
00877         iCrcTableCalculated = ETrue;
00878         }

Generated by  doxygen 1.6.2