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 }
Copyright ©2010 Nokia Corporation and/or its subsidiary(-ies).
All rights
reserved. Unless otherwise stated, these materials are provided under the terms of the Eclipse Public License
v1.0.