Symbian
Symbian OS Library

SYMBIAN OS V9.3

[Index] [Spacer] [Previous] [Next]



ICLCodec: Image Converter Library encoder/decoder plug-ins example

Found in: Examples\MultiMedia\ICLCodec\

These are the main files contained in the examples. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

//
// ImageUtils.h
// Copyright (c) Symbian 2002. All rights reserved.
//

#ifndef __ImageUtils_h
#define __ImageUtils_h


//
// PtrReadUtil - utility class with methods for standard 
// reading data from a TUint8* string
//

class PtrReadUtil
    {
public:
    // This calls decode from TUint8*
    static TInt8 ReadInt8(const TUint8* aPtr);
    static TUint8 ReadUint8(const TUint8* aPtr);
    static TInt16 ReadInt16(const TUint8* aPtr);
    static TInt16 ReadBigEndianInt16(const TUint8* aPtr);
    static TUint16 ReadUint16(const TUint8* aPtr);
    static TUint16 ReadBigEndianUint16(const TUint8* aPtr);
    static TInt32 ReadInt32(const TUint8* aPtr);
    static TInt32 ReadBigEndianInt32(const TUint8* aPtr);
    static TUint32 ReadUint32(const TUint8* aPtr);
    static TUint32 ReadBigEndianUint32(const TUint8* aPtr);
    // these calls also increment the pointer
    static TInt8 ReadInt8Inc(const TUint8*& aPtr);
    static TUint8 ReadUint8Inc(const TUint8*& aPtr);
    static TInt16 ReadInt16Inc(const TUint8*& aPtr);
    static TInt16 ReadBigEndianInt16Inc(const TUint8*& aPtr);
    static TUint16 ReadUint16Inc(const TUint8*& aPtr);
    static TUint16 ReadBigEndianUint16Inc(const TUint8*& aPtr);
    static TInt32 ReadInt32Inc(const TUint8*& aPtr);
    static TInt32 ReadBigEndianInt32Inc(const TUint8*& aPtr);
    static TUint32 ReadUint32Inc(const TUint8*& aPtr);
    static TUint32 ReadBigEndianUint32Inc(const TUint8*& aPtr);
    };

inline TUint8 PtrReadUtil::ReadUint8(const TUint8* aPtr)
    {
    return *aPtr ;
    }

inline TInt8 PtrReadUtil::ReadInt8(const TUint8* aPtr)
    {
    return TInt8(ReadUint8(aPtr));
    }

inline TUint16 PtrReadUtil::ReadUint16(const TUint8* aPtr)
    {
    return TUint16(aPtr[0] | (aPtr[1]<<8));
    }

inline TInt16 PtrReadUtil::ReadInt16(const TUint8* aPtr)
    {
    return TInt16(ReadUint16(aPtr));
    }

inline TUint32 PtrReadUtil::ReadUint32(const TUint8* aPtr)
    {
    return TUint32(aPtr[0] | (aPtr[1]<<8) | (aPtr[2]<<16) | (aPtr[3]<<24));
    }

inline TInt32 PtrReadUtil::ReadInt32(const TUint8* aPtr)
    {
    return TInt32(ReadUint32(aPtr));
    }

inline TUint16 PtrReadUtil::ReadBigEndianUint16(const TUint8* aPtr)
    {
    return TUint16((aPtr[0]<<8) | aPtr[1]);
    }

inline TInt16 PtrReadUtil::ReadBigEndianInt16(const TUint8* aPtr)
    {
    return TInt16(ReadBigEndianInt16(aPtr));
    }

inline TUint32 PtrReadUtil::ReadBigEndianUint32(const TUint8* aPtr)
    {
    return TUint32((aPtr[0]<<24) | (aPtr[1]<<16) | (aPtr[2]<<8) | aPtr[3]);
    }

inline TInt32 PtrReadUtil::ReadBigEndianInt32(const TUint8* aPtr)
    {
    return TInt32(ReadBigEndianInt32(aPtr));
    }

inline TInt8 PtrReadUtil::ReadInt8Inc(const TUint8*& aPtr)
    {
    TInt8 result = ReadInt8(aPtr);
    aPtr += 1;
    return result;
    }

inline TUint8 PtrReadUtil::ReadUint8Inc(const TUint8*& aPtr)
    {
    TUint8 result = ReadUint8(aPtr);
    aPtr += 1;
    return result;
    }

inline TInt16 PtrReadUtil::ReadInt16Inc(const TUint8*& aPtr)
    {
    TInt16 result = ReadInt16(aPtr);
    aPtr += 2;
    return result;
    }

inline TUint16 PtrReadUtil::ReadUint16Inc(const TUint8*& aPtr)
    {
    TUint16 result = ReadUint16(aPtr);
    aPtr += 2;
    return result;
    }

inline TInt16 PtrReadUtil::ReadBigEndianInt16Inc(const TUint8*& aPtr)
    {
    TInt16 result = ReadBigEndianInt16(aPtr);
    aPtr += 2;
    return result;
    }

inline TUint16 PtrReadUtil::ReadBigEndianUint16Inc(const TUint8*& aPtr)
    {
    TUint16 result = ReadBigEndianUint16(aPtr);
    aPtr += 2;
    return result;
    }

inline TInt32 PtrReadUtil::ReadInt32Inc(const TUint8*& aPtr)
    {
    TInt32 result = ReadInt32(aPtr);
    aPtr += 4;
    return result;
    }

inline TUint32 PtrReadUtil::ReadUint32Inc(const TUint8*& aPtr)
    {
    TUint32 result = ReadUint32(aPtr);
    aPtr += 4;
    return result;
    }

inline TInt32 PtrReadUtil::ReadBigEndianInt32Inc(const TUint8*& aPtr)
    {
    TInt32 result = ReadBigEndianInt32(aPtr);
    aPtr += 4;
    return result;
    }

inline TUint32 PtrReadUtil::ReadBigEndianUint32Inc(const TUint8*& aPtr)
    {
    TUint32 result = ReadBigEndianUint32(aPtr);
    aPtr += 4;
    return result;
    }

class PtrWriteUtil
    {
public:
    static void WriteInt8(TUint8* aPtr, TInt aData);
    static void WriteInt16(TUint8* aPtr, TInt aData);
    static void WriteInt32(TUint8* aPtr, TInt aData);
    // Big endian version
    static void WriteBigEndianInt32(TUint8* aPtr, TInt32 aData);
    };

inline void PtrWriteUtil::WriteInt8(TUint8* aPtr, TInt aData)
    {
    aPtr[0] = TUint8(aData);
    }

inline void PtrWriteUtil::WriteInt16(TUint8* aPtr, TInt aData)
    {
    aPtr[0] = TUint8(aData);
    aPtr[1] = TUint8(aData>>8);
    }

inline void PtrWriteUtil::WriteInt32(TUint8* aPtr, TInt aData)
    {
    aPtr[0] = TUint8(aData);
    aPtr[1] = TUint8(aData>>8);
    aPtr[2] = TUint8(aData>>16);
    aPtr[3] = TUint8(aData>>24);
    }

inline void PtrWriteUtil::WriteBigEndianInt32(TUint8* aPtr, TInt32 aData)
    {
    aPtr[0] = TUint8(aData>>24);
    aPtr[1] = TUint8(aData>>16);
    aPtr[2] = TUint8(aData>>8);
    aPtr[3] = TUint8(aData);
    }

#endif // __ImageUtils_h

// PngCodec.H
//
// Copyright (c) 1999-2005 Symbian Software Ltd.  All rights reserved.
//

#ifndef __PNGCODEC_H__
#define __PNGCODEC_H__

#include <bitdev.h>
#include <ezcompressor.h>
#include <ezdecompressor.h>
#include <ICL/ImageProcessor.h>
#include <ICL/ImageCodec.h>

#include "PngConvert.h"

//.
// Constants.relating to PNG standard 
//
const TInt KPngFileSignatureLength = 8;
const TUint8 KPngSignature[KPngFileSignatureLength] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };

const TInt KPngChunkIdSize = 4;
const TInt KPngMaxPLTESize = 256;
const TInt KPngNumInterlacedPasses = 8; // 7 passes plus a safety entry

const TInt KPngChunkLengthSize = 4;
const TInt KPngChunkCRCSize = 4;

const TInt KPngIHDRChunkSize = 13;
const TInt KPngIENDChunkSize = 0;
const TInt KPngcHRMChunkSize = 32;
const TInt KPnggAMAChunkSize = 4;
const TInt KPngpHYsChunkSize = 9;
const TInt KPngtIMEChunkSize = 7;

// KPngMaxImageSize is the maximum size for width and height, as defined in the PNG Specification v1.0 page 14
const TUint KPngMaxImageSize = ((TUint)2 << 30) - 1;
const TInt KPngCrcTableLength = 256;
const TUint KPngCrcMask = 0xffffffff;

_LIT8(KPngIHDRChunkId,"IHDR");
_LIT8(KPngPLTEChunkId,"PLTE");
_LIT8(KPngIDATChunkId,"IDAT");
_LIT8(KPngIENDChunkId,"IEND");
_LIT8(KPngbKGDChunkId,"bKGD");
_LIT8(KPngpHYsChunkId,"pHYs");
_LIT8(KPngtRNSChunkId,"tRNS");


// .
// Encapsulates information about a PNG image
// See PNG standard for details.
// 
class TPngImageInformation : public TFrameInfo
    {
public:
    TPngImageInformation();
public:
    enum TColorElements
        {
        EMonochrome = 0x0,
        EPaletteUsed = 0x1,
        EColorUsed = 0x2,
        EAlphaChannelUsed = 0x4
        };
    enum TColorType
        {
        EGrayscale = EMonochrome, // 0
        EDirectColor = EColorUsed, // 2
        EIndexedColor = EColorUsed | EPaletteUsed, // 3
        EAlphaGrayscale = EMonochrome | EAlphaChannelUsed, // 4
        EAlphaDirectColor = EColorUsed | EAlphaChannelUsed // 6
        };
    enum TCompressionMethod
        {
        EDeflateInflate32K = 0
        };
    enum TFilterMethod
        {
        EAdaptiveFiltering = 0
        };
    enum TInterlaceMethod
        {
        ENoInterlace = 0,
        EAdam7Interlace = 1
        };
    enum TPhysicalUnits
        {
        EUnknownUnits = 0,
        EMeters = 1
        };
public:
    // IHDR chunk
    TSize iSize; // iWidth/iHeight = 1 ... (2^31)-1
    TInt iBitDepth; // 1,2,4,8,16 subject to color type restrictions
    TColorType iColorType; // 0,2,3,4,6
    TCompressionMethod iCompressionMethod; // 0
    TFilterMethod iFilterMethod; // 0
    TInterlaceMethod iInterlaceMethod; // 0 or 1

    // PLTE chunk
    TBool iPalettePresent;
    TRgb iPalette[KPngMaxPLTESize];

    // bKGD chunk
    TBool iBackgroundPresent;

    // pHYs chunk
    TBool iPhysicalPresent;
    TPhysicalUnits iPhysicalUnits;
    TSize iPhysicalSize;

    // tRNS chunk
    TBool iTransparencyPresent;
    TUint16 iTransparentGray;
    TUint16 iTransparentRed;
    TUint16 iTransparentGreen;
    TUint16 iTransparentBlue;
    TUint8 iTransparencyValue[KPngMaxPLTESize];
    };

//
// Handles reading raw PNG scanlines and preparing the data
// to be decompressed. A helper to CPngReadCodec.
// It is abstract, and subclassed to implement readers for 
// scanlines of different bitmap depths
//
class CPngReadSubCodec : public CBase
    {
public:
    static CPngReadSubCodec* NewL(CImageProcessor* aImageProc,CImageProcessor* aMaskProc,const TPngImageInformation& aInfo);
    virtual ~CPngReadSubCodec();
    TDes8& FirstBuffer();
    TDes8& DecodeL();
protected:
    CPngReadSubCodec();
    void WritePixel(TRgb aPixelColor);
    void WritePixel(TRgb aPixelColor,TUint8 aAlphaValue);
private:
    void ConstructL(CImageProcessor* aImageProc,CImageProcessor* aMaskProc,const TPngImageInformation& aInfo);
    void FilterScanlineDataL(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    TInt PaethPredictor(TInt aLeft,TInt aAbove,TInt aAboveLeft);
    virtual void DoConstructL() = 0;
    virtual TInt ScanlineBufferSize(TInt aPixelLength) = 0;
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit) = 0;
    void UpdatePos();
protected:
    CImageProcessor* iImageProc;
    CImageProcessor* iMaskProc;
    TPngImageInformation iInfo;
    TInt iScanlineBufferSize;
    TInt iBytesPerPixel;
    HBufC8* iScanlineBuffer1;
    HBufC8* iScanlineBuffer2;
    TPtr8 iScanlineDes1;
    TPtr8 iScanlineDes2;
    TInt iCurrentScanlineBuffer;
    TInt iInterlacedScanlineBufferSize[KPngNumInterlacedPasses];
    TInt iPass;
    TPoint iPos;
    };

class CPngDecoder;

//
// Codec class that does the real work of reading a PNG image.
// Inherits base classes that provide image mask processing
// and interface to unzip functionality
//
class CPngReadCodec : public CImageMaskProcessorReadCodec, public MEZBufferManager
    {
public:
    // Construction
    ~CPngReadCodec();
    void ConstructL () { CImageMaskProcessorReadCodec::ConstructL(); } // make public, since we don't have NewL

private:
    // From CImageReadCodec
    // Initialise frame to read
    void InitFrameL(TFrameInfo& aFrameInfo, CFrameImageData& aFrameImageData, 
        TBool aDisableErrorDiffusion, CFbsBitmap& aDestination, CFbsBitmap* aDestinationMask);
    // Initialise from header
    void InitFrameHeader(TFrameInfo& aFrameSettings, CFrameImageData& /* aFrameImageData */);
    // Process header
    TFrameState ProcessFrameHeaderL(TBufPtr8& aData);
    // Process frame
    TFrameState ProcessFrameL(TBufPtr8& aSrc);

private:
    // Helper functions to process PNG chunk types
    void DoNewFrameL(CPngDecoder* aPngDecoder);
    void DoProcessInfoL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit);
    void DoProcessIHDRL(const TUint8* aDataPtr,TInt aChunkLength);
    void DoProcessPLTEL(const TUint8* aDataPtr,TInt aChunkLength);
    void DoProcessbKGDL(const TUint8* aDataPtr,TInt aChunkLength);
    void DoProcesspHYsL(const TUint8* aDataPtr,TInt aChunkLength);
    void DoProcesstRNSL(const TUint8* aDataPtr,TInt aChunkLength);
    void DoProcessDataL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit);

    // From MEZBufferManager: feed data to unzip
    void InitializeL(CEZZStream &aZStream);
    void NeedInputL(CEZZStream &aZStream);
    void NeedOutputL(CEZZStream &aZStream);
    void FinalizeL(CEZZStream &aZStream);

private:
    TFrameInfo* iFrameInfo;
    TPngImageInformation iImageInfo;
    TBuf8<KPngChunkIdSize> iChunkId;
    TInt iChunkBytesRemaining;
    CPngReadSubCodec* iDecoder;
    CEZDecompressor* iDecompressor;
    TPtrC8 iDataDes;
    };

//
// Handles preparing image data
// for compression and writing raw scanlines 
// A helper to CPngWriteCodec
// 
class CPngWriteSubCodec : public CBase
    {
public:
    static CPngWriteSubCodec* NewL(const TPngImageInformation& aInfo, const CFbsBitmap* aSource);
    virtual ~CPngWriteSubCodec();
protected:
    CPngWriteSubCodec();
private:
    void ConstructL(const TPngImageInformation& aInfo, const CFbsBitmap* aSource);
public:
    TDes8& Buffer();
    inline TInt BufferSize() const;
    TDes8& EncodeL(const TInt aScanline);
    inline CPalette* Palette() const;
protected:
    TUint8 ReverseBits(const TUint8 aValue) const;
    void EncodePalettedScanline(TUint8* aDataPtr, const CFbsBitmap* aSource, const TInt aScanline,
                                const TInt aPixelsPerByte, const TInt aShiftValue);
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength) = 0;
    virtual void DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                    TUint8* aDataPtr, const TUint8* aDataPtrLimit) = 0;
protected:
    TPngImageInformation iInfo;
    const CFbsBitmap* iSource;
    TInt iScanlineBufferSize;
    HBufC8* iScanlineBuffer;
    TPtr8 iScanlineDes;
    CPalette* iPalette;
    };

//
// Codec class that does the real work of writing a PNG image.
// Inherits base classes to interface to zip functionality
//
class CPngWriteCodec : public CImageWriteCodec, public MEZBufferManager
    {
public:
    // Construction
    CPngWriteCodec(TInt aBpp, TBool aColor, TBool aPaletted, TInt aCompressionLevel);
    void ConstructL () { CImageWriteCodec::ConstructL(); } // make public, since we don't have NewL
    ~CPngWriteCodec();

private:
    // from CImageWriteCodec
    void InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource);
    TFrameState ProcessFrameL(TBufPtr8& aDst);

private:
    // Helper functions to write PNG chunks
    void WritePngChunk(TUint8*& aDestPtr, const TDesC8& aChunkId, const TDesC8& aData, TInt& aLength);
    void DeflateEncodedDataL(TBufPtr8& aDst, TFrameState& aState);
    void FlushCompressedDataL(TBufPtr8& aDst, TFrameState& aState);
    void WritePLTEChunk(TBufPtr8& aDst);
    void WriteIDATChunk(TBufPtr8& aDst);
    void WriteEndChunk(TBufPtr8& aDst);
    TInt WriteHeaderChunk(TBufPtr8& aDst);
    void GetImageDataL(TInt& aBytesToProcess);
    void GetPngScanLine(TDes8& aBuf, const TPoint& aPixel, TInt& aLength);
    void CalcCrcTable();
    void GetCrc(TUint32& aCrc, const TUint8* aPtr, const TInt aLength);

private:
    // from MEZBufferManager
    // feeds data to zip
    void InitializeL(CEZZStream &aZStream);
    void NeedInputL(CEZZStream &aZStream);
    void NeedOutputL(CEZZStream &aZStream);
    void FinalizeL(CEZZStream &aZStream);

private:
    enum TPngEncoderState
        {
        EPngDeflate,    // Deflate encoded image data
        EPngFlush,      // Flush encoded image data
        EPngWritePLTE,  // Write PLTE chunk
        EPngWriteIDAT,  // Write IDAT chunk
        EPngEndChunk    // Write end chunk
        };
    
private:
    TUint32 iCrcTable[KPngCrcTableLength];
    TBool iCrcTableCalculated;
    CPngWriteSubCodec* iEncoder;
    TPngEncoderState iEncoderState;
    TInt iCompressionLevel;
    CEZCompressor* iCompressor;
    TInt iScanline;
    TUint8* iDestStartPtr;
    TUint8* iDestPtr;
    TUint8* iDestPtrLimit;
    TPngImageInformation iImageInfo;
    TPtr8 iCompressorPtr;  // data area for compressor to write to
    TBool iCallAgain;
    };


// Global panic function
void Panic(TInt aError);

//
// inline definitions
//
inline TInt CPngWriteSubCodec::BufferSize() const
    { return iScanlineBufferSize; }

inline CPalette* CPngWriteSubCodec::Palette() const
    { return iPalette; }


#endif // __PNGCODEC_H__

// PngConvert.H
//
// Copyright (c) 1999-2005 Symbian Software Ltd.  All rights reserved.
//

#ifndef __PNGCONVERT_H__
#define __PNGCONVERT_H__

#include <ICL/ImagePlugin.h>

#include "PngCodec.h"

//
// PNG decoder class
//
class CPngDecoder : public CImageDecoderPlugin
    {
public:
    // Construction
   static CPngDecoder* NewL();
    ~CPngDecoder();

private:
    // from CImageDecoderPlugin
    
    // Gets the image type and sub-type for a given frame of the image that
    // has just been decoded.
    virtual void ImageType(TInt aFrameNumber, TUid& aImageType, TUid& aImageSubType) const; 
    // Gets text descriptions of image properties
    CFrameInfoStrings* FrameInfoStringsL(RFs& aFs, TInt aFrameNumber);
    // Scans the image header.
    void ScanDataL();

private:
    CPngDecoder();
    void ReadFormatL();
    };

//
// PNG encoder class
//
class CPngEncoder : public CImageEncoderPlugin
    {
public:
    // Construction
    static CPngEncoder* NewL();
     ~CPngEncoder();

private:
    // from CImageEncoderPlugin

    // Sets up the codec to encode the frame
    void PrepareEncoderL(const CFrameImageData* aFrameImageData);
    // Update header (not used)
    void UpdateHeaderL() {};

private:
    CPngEncoder();
    };

#endif // __PNGCONVERT_H__

/ PngScanlineEncoder.h
//
// Copyright (c) 1997-2005 Symbian Software Ltd.  All rights reserved.
//

#ifndef __PNGSCANLINEENCODER_H__
#define __PNGSCANLINEENCODER_H__

#include "PngCodec.h"
#include "ImageUtils.h"

//
// These classes specialise the PNG writer to write
// scanlines with different bitmap depths/colour types
//

const TInt KPngScanlineFilterTypeLength = 1;

//
// CBitDepth1Encoder: specialised writer for 1bpp
//

const TInt KPngDepth1PixelsPerByte = 8;
const TInt KPngDepth1RoundUpValue = 7;
const TInt KPngDepth1ShiftValue = 1;

class CBitDepth1Encoder : public CPngWriteSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                    TUint8* aDataPtr, const TUint8* aDataPtrLimit);
    };

//
// CBitDepth2Encoder: specialised writer for 2bpp
//

const TInt KPngDepth2PixelsPerByte = 4;
const TInt KPngDepth2RoundUpValue = 3;
const TInt KPngDepth2ShiftValue = 2;
class CBitDepth2Encoder : public CPngWriteSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                    TUint8* aDataPtr, const TUint8* aDataPtrLimit);
    };

//
// CBitDepth4Encoder: specialised writer for 4bpp
//

const TInt KPngDepth4PixelsPerByte = 2;
const TInt KPngDepth4RoundUpValue = 1;
const TInt KPngDepth4ShiftValue = 4;
class CBitDepth4Encoder : public CPngWriteSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                    TUint8* aDataPtr, const TUint8* aDataPtrLimit);
    };

//
// CBitDepth8ColorType2Encoder: specialised writer for 8bpp, RGB colour
//
const TInt KPngDepth8RgbBytesPerPixel = 3;
class CBitDepth8ColorType2Encoder : public CPngWriteSubCodec
    {
private:
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                    TUint8* aDataPtr, const TUint8* aDataPtrLimit);
    };

//
// CBitDepth8Encoder: specialised writer for 8bpp
//

const TInt KPngDepth8PixelsPerByte = 1;
const TInt KPngDepth8ShiftValue = 0;
class CBitDepth8Encoder : public CPngWriteSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                    TUint8* aDataPtr, const TUint8* aDataPtrLimit);
    };

#endif // __PNGSCANLINEENCODER_H__

// Uids.h
//
// Copyright (c) 1999-2005 Symbian Software Ltd.  All rights reserved.
//

#ifndef __PNGUIDS_H__
#define __PNGUIDS_H__

// DLL and implementation UIDs
#define KExPNGCodecDllUidValue             0x101F4122
#define KExPNGDecoderImplementationUidValue    0x101F4123
#define KExPNGEncoderImplementationUidValue    0x101F4124

#endif

// PngCodec.CPP
//
// Copyright (c) 1999-2005 Symbian Software Ltd.  All rights reserved.
//

#include <fbs.h>

#include "ImageUtils.h"
#include "PngCodec.h"

// Constants.
const TInt KTwipsPerMeter = 56693;

// 
// TPngImageInformation: PNG image information
//

// Initialise default PNG image information
TPngImageInformation::TPngImageInformation()
    {
    iSize.SetSize(0,0);
    iBitDepth = 0;
    iColorType = EGrayscale;
    iCompressionMethod = EDeflateInflate32K;
    iFilterMethod = EAdaptiveFiltering;
    iInterlaceMethod = ENoInterlace;
    iPalettePresent = EFalse;

#if defined(_DEBUG)
    // as an optimisation, we are going to set iPalette to all zeros, instead of setting
    // each element to KRgbBlack. This assumes that KRbgBlack is itself zero.
    ASSERT(sizeof(TRgb)==sizeof(TUint32)); // ie no new fields
    ASSERT(KRgbBlack.Value()==0); // ie the one value is zero
#endif // defined(_DEBUG)

    Mem::FillZ(iPalette, KPngMaxPLTESize*sizeof(TRgb));

    iBackgroundPresent = EFalse;
    iBackgroundColor = KRgbWhite;
    iPhysicalPresent = EFalse;
    iPhysicalUnits = EUnknownUnits;
    iPhysicalSize.SetSize(0,0);
    iTransparencyPresent = EFalse;
    Mem::Fill(iTransparencyValue,KPngMaxPLTESize,0xff);
    }


//
// CPngReadCodec: reads a PNG image
//

CPngReadCodec::~CPngReadCodec()
    {
    delete iDecoder;
    delete iDecompressor;
    }

// Called by framework when a Convert operation begins
void CPngReadCodec::InitFrameL(TFrameInfo& /*aFrameInfo*/, CFrameImageData& /*aFrameImageData*/, 
    TBool aDisableErrorDiffusion, CFbsBitmap& aDestination, CFbsBitmap* aDestinationMask)
    {
    CFbsBitmap& newFrame = aDestination;

    TPoint& pos = Pos();
    pos.SetXY(0,0);
    iChunkBytesRemaining = 0;

    // Use the supplied image processor
    CImageProcessor* imageProc = ImageProcessorUtility::NewImageProcessorL(newFrame, iImageInfo.iSize, ERgb, aDisableErrorDiffusion);
    SetImageProcessor(imageProc);
    imageProc->PrepareL(newFrame,iImageInfo.iSize);

    CImageProcessor* maskProc = NULL;
    SetMaskProcessor(NULL);

    // If transparency is being used, create a bitmap mask as well
    if ((iImageInfo.iTransparencyPresent || (iImageInfo.iColorType & TPngImageInformation::EAlphaChannelUsed))
        && aDestinationMask)
        {
        maskProc = ImageProcessorUtility::NewImageProcessorL(*aDestinationMask, iImageInfo.iSize, ERgb, aDisableErrorDiffusion);
        SetMaskProcessor(maskProc);
        maskProc->PrepareL(*aDestinationMask,iImageInfo.iSize);
        // set mask to black so that unknown parts on streamed image are not drawn
        ClearBitmapL(*aDestinationMask, KRgbBlack);
        }

    // Create a helper to read the scan lines
    delete iDecoder;
    iDecoder = NULL;
    iDecoder = CPngReadSubCodec::NewL(imageProc,maskProc,iImageInfo);

    // And a unzipper to decompress image data
    if (!iDecompressor)
        iDecompressor = CEZDecompressor::NewL(*this);
    else
        iDecompressor->ResetL(*this);

    if (maskProc==NULL)
        {
        // if no mask, clear destination for sensible behaviour on streamed partial images
        TRgb background = iImageInfo.iBackgroundPresent ? iImageInfo.iBackgroundColor : KRgbWhite;
        ClearBitmapL(aDestination, background);
        }
    }

// Called by framework to initialise image frame header
void CPngReadCodec::InitFrameHeader(TFrameInfo& aFrameSettings, CFrameImageData& /* aFrameImageData */)
    {
    ASSERT(aFrameSettings.CurrentFrameState() == TFrameInfo::EFrameInfoUninitialised);
    iFrameInfo = &aFrameSettings;
    iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrameHeader);
    }

// Called by framework to process a header for a frame
TFrameState CPngReadCodec::ProcessFrameHeaderL(TBufPtr8& aData)
    {
    const TUint8* startDataPtr = aData.Ptr();
    const TUint8* dataPtr = startDataPtr;
    const TUint8* dataPtrLimit = startDataPtr + aData.Length();

    // Process the mandatory PNG header chunk: sets up iImageInfo
    if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrameHeader)
        {
        if (dataPtr + KPngChunkLengthSize + KPngChunkIdSize + KPngIHDRChunkSize + KPngChunkCRCSize > dataPtrLimit)
            User::Leave(KErrUnderflow);

        TInt chunkLength = PtrReadUtil::ReadBigEndianUint32Inc(dataPtr);
        TPtrC8 chunkId(dataPtr,KPngChunkIdSize);

        if (chunkLength != KPngIHDRChunkSize || chunkId != KPngIHDRChunkId)
            User::Leave(KErrNotFound);

        dataPtr += KPngChunkIdSize;

        DoProcessIHDRL(dataPtr,chunkLength);

        dataPtr += KPngIHDRChunkSize + KPngChunkCRCSize;
        }

    // Process any optional PNG header chunks
    TRAPD(err, DoProcessInfoL(dataPtr, dataPtrLimit));
    if (err != KErrNone)
        {
        if (err == KErrNotFound)
            return EFrameComplete;

        User::Leave(err); // A real error occured
        }

    // Having read the header, can initialise the frame information
    aData.Shift(dataPtr - startDataPtr);

    iFrameInfo->iFrameCoordsInPixels.SetRect(TPoint(0,0),iImageInfo.iSize);
    iFrameInfo->iOverallSizeInPixels = iImageInfo.iSize;
    if (iImageInfo.iPhysicalPresent && iImageInfo.iPhysicalUnits == TPngImageInformation::EMeters)
        iFrameInfo->iFrameSizeInTwips = iImageInfo.iPhysicalSize;
    else
        iFrameInfo->iFrameSizeInTwips.SetSize(0,0);

    iFrameInfo->iBitsPerPixel = iImageInfo.iBitDepth;
    if (iImageInfo.iColorType & TPngImageInformation::EColorUsed)
        iFrameInfo->iBitsPerPixel *= 3;
    
    iFrameInfo->iDelay = 0;
    iFrameInfo->iFlags = TFrameInfo::ECanDither;
    
    if (iImageInfo.iColorType & (TPngImageInformation::EPaletteUsed | TPngImageInformation::EColorUsed))
        iFrameInfo->iFlags |= TFrameInfo::EColor;
    
    if (iImageInfo.iColorType & TPngImageInformation::EAlphaChannelUsed)
        {
        iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;
        iFrameInfo->iFlags |= TFrameInfo::EAlphaChannel;
        }
    else if (iImageInfo.iTransparencyPresent)
        iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;

    switch (iFrameInfo->iBitsPerPixel)
        {
    case 1:
        iFrameInfo->iFrameDisplayMode = EGray2;
        break;

    case 2:
        iFrameInfo->iFrameDisplayMode = EGray4;
        break;

    case 4:
        iFrameInfo->iFrameDisplayMode = (iFrameInfo->iFlags & TFrameInfo::EColor) ? EColor16 : EGray16;
        break;

    case 8:
        iFrameInfo->iFrameDisplayMode = (iFrameInfo->iFlags & TFrameInfo::EColor) ? EColor256 : EGray256;
        break;

    case 12:
        iFrameInfo->iFrameDisplayMode = EColor4K;
        break;

    case 16:
        iFrameInfo->iFrameDisplayMode = EColor64K;
        break;

    case 24:
        iFrameInfo->iFrameDisplayMode = EColor16M;
        break;

    default:
        User::Leave(KErrCorrupt);
        }

    if (iImageInfo.iBackgroundPresent)
        iFrameInfo->iBackgroundColor = iImageInfo.iBackgroundColor;

    iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete);
    return EFrameComplete;
    }

// Called by the framework to process frame data
TFrameState CPngReadCodec::ProcessFrameL(TBufPtr8& aSrc)
    {
    CImageProcessor*const imageProc = ImageProcessor();
    CImageProcessor*const maskProc = MaskProcessor();

    TUint8* startDataPtr = const_cast<TUint8*>(aSrc.Ptr());
    TUint8* dataPtr = startDataPtr;
    const TUint8* dataPtrLimit = dataPtr + aSrc.Length();
    while (dataPtr < dataPtrLimit)
        {
        // If at the end of a PNG chunk
        if (iChunkBytesRemaining == 0)
            {
            if (iChunkId == KPngIDATChunkId) // Need to skip IDAT chunk CRCs
                {
                if (dataPtr + KPngChunkCRCSize + KPngChunkLengthSize + KPngChunkIdSize > dataPtrLimit)
                    break;

                dataPtr += KPngChunkCRCSize;
                }
            else
                {
                if (dataPtr + KPngChunkLengthSize + KPngChunkIdSize > dataPtrLimit)
                    break;
                }

            iChunkBytesRemaining = PtrReadUtil::ReadBigEndianUint32Inc(const_cast<const TUint8*&>(dataPtr));
            iChunkId = TPtr8(dataPtr,KPngChunkIdSize,KPngChunkIdSize);
            dataPtr += KPngChunkIdSize;
            }
        // Process an image data chunk
        if (iChunkId == KPngIDATChunkId)
            DoProcessDataL(const_cast<const TUint8*&>(dataPtr),dataPtrLimit);
        // Process an END chunk -- frame is complete
        else if (iChunkId == KPngIENDChunkId)
            {
            iDecompressor->InflateL();
            imageProc->FlushPixels();
            if (maskProc)
                maskProc->FlushPixels();
            return EFrameComplete;
            }
        else 
        // Skip other chunks
            {
            TInt bytesLeft = dataPtrLimit - dataPtr;
            if (bytesLeft >= iChunkBytesRemaining + KPngChunkCRCSize)
                {
                dataPtr += iChunkBytesRemaining + KPngChunkCRCSize;
                iChunkBytesRemaining = 0;
                }
            else
                {
                dataPtr += bytesLeft;
                iChunkBytesRemaining -= bytesLeft;
                }
            }
        }

    aSrc.Shift(dataPtr - startDataPtr);
    return EFrameIncomplete;
    }

// Process any optional PNG header chunks
void CPngReadCodec::DoProcessInfoL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
    {
    FOREVER
        {
        if (aDataPtr + KPngChunkLengthSize + KPngChunkIdSize > aDataPtrLimit) // Check there is enough data to read the chunk length
            User::Leave(KErrUnderflow);

        TInt chunkLength = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
        TPtrC8 chunkId (&aDataPtr[0],KPngChunkIdSize);

        if (chunkId == KPngIDATChunkId)
            {
            aDataPtr -= KPngChunkLengthSize; // Rewind to start of chunkLength
            break;
            }

        if (aDataPtr + KPngChunkIdSize + chunkLength + KPngChunkCRCSize > aDataPtrLimit) // Check there is enough data to read the whole chunk
            {
            aDataPtr -= KPngChunkLengthSize; // Rewind to start of chunkLength
            User::Leave(KErrUnderflow);
            }

        aDataPtr += KPngChunkIdSize;

        if (chunkId == KPngPLTEChunkId)
            DoProcessPLTEL(aDataPtr,chunkLength);
        else if (chunkId == KPngbKGDChunkId)
            DoProcessbKGDL(aDataPtr,chunkLength);
        else if (chunkId == KPngpHYsChunkId)
            DoProcesspHYsL(aDataPtr,chunkLength);
        else if (chunkId == KPngtRNSChunkId)
            DoProcesstRNSL(aDataPtr,chunkLength);
        else if (chunkId == KPngIHDRChunkId || chunkId == KPngIENDChunkId)
            User::Leave(KErrCorrupt);

        aDataPtr += chunkLength;
        PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr); // Skip crc value
        }
    }

// Process the mandatory PNG header chunk
void CPngReadCodec::DoProcessIHDRL(const TUint8* aDataPtr,TInt aChunkLength)
    {
    if (aChunkLength != KPngIHDRChunkSize)
        User::Leave(KErrCorrupt);

    iImageInfo.iSize.iWidth = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
    iImageInfo.iSize.iHeight = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
    iImageInfo.iBitDepth = aDataPtr[0];
    iImageInfo.iColorType = TPngImageInformation::TColorType(aDataPtr[1]);
    iImageInfo.iCompressionMethod = TPngImageInformation::TCompressionMethod(aDataPtr[2]);
    iImageInfo.iFilterMethod = TPngImageInformation::TFilterMethod(aDataPtr[3]);
    iImageInfo.iInterlaceMethod = TPngImageInformation::TInterlaceMethod(aDataPtr[4]);

    // Check is one of the PNG formats we support
    if (iImageInfo.iSize.iWidth < 1 || iImageInfo.iSize.iHeight < 1
        || iImageInfo.iCompressionMethod != TPngImageInformation::EDeflateInflate32K
        || iImageInfo.iFilterMethod != TPngImageInformation::EAdaptiveFiltering
        || (iImageInfo.iInterlaceMethod != TPngImageInformation::ENoInterlace &&
            iImageInfo.iInterlaceMethod != TPngImageInformation::EAdam7Interlace))
        User::Leave(KErrCorrupt);
    }

// Process a PNG PLTE (palette) chunk
void CPngReadCodec::DoProcessPLTEL(const TUint8* aDataPtr,TInt aChunkLength)
    {
    if (aChunkLength % 3 != 0)
        User::Leave(KErrCorrupt);

    iImageInfo.iPalettePresent = ETrue;

    const TUint8* dataPtrLimit = aDataPtr + aChunkLength;
    TRgb* palettePtr = iImageInfo.iPalette;

    while (aDataPtr < dataPtrLimit)
        {
        *palettePtr++ = TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]);
        aDataPtr += 3;
        }
    }

// Process a PNG bKGD (background color) chunk
void CPngReadCodec::DoProcessbKGDL(const TUint8* aDataPtr,TInt aChunkLength)
    {
    iImageInfo.iBackgroundPresent = ETrue;

    if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor) // 3
        {
        if (aChunkLength < 1)
            User::Leave(KErrCorrupt);

        iImageInfo.iBackgroundColor = iImageInfo.iPalette[aDataPtr[0]];
        }
    else if (iImageInfo.iColorType & TPngImageInformation::EMonochrome) // 0 & 4
        {
        if (aChunkLength < 2)
            User::Leave(KErrCorrupt);

        TInt grayLevel = PtrReadUtil::ReadBigEndianInt16(aDataPtr);
        ASSERT(iImageInfo.iBitDepth<8);
        grayLevel <<= (7-iImageInfo.iBitDepth);
        iImageInfo.iBackgroundColor = TRgb::Gray256(grayLevel);
        }
    else if (iImageInfo.iColorType & TPngImageInformation::EColorUsed) // 2 & 6
        {
        if (aChunkLength < 6)
            User::Leave(KErrCorrupt);

        TInt red = PtrReadUtil::ReadBigEndianInt16(&aDataPtr[0]);
        TInt green = PtrReadUtil::ReadBigEndianInt16(&aDataPtr[2]);
        TInt blue = PtrReadUtil::ReadBigEndianInt16(&aDataPtr[4]);
        ASSERT (iImageInfo.iBitDepth<8);
        const TInt offset = 7-iImageInfo.iBitDepth;
        red <<= offset;
        green <<= offset;
        blue <<= offset;
        iImageInfo.iBackgroundColor = TRgb(red,green,blue);
        }
    }

// Process a PNG pHYs (Physical pixel dimensions) chunk
void CPngReadCodec::DoProcesspHYsL(const TUint8* aDataPtr,TInt aChunkLength)
    {
    if (aChunkLength != KPngpHYsChunkSize)
        User::Leave(KErrCorrupt);

    iImageInfo.iPhysicalUnits = TPngImageInformation::TPhysicalUnits(aDataPtr[8]);

    if (iImageInfo.iPhysicalUnits == TPngImageInformation::EMeters)
        {
        iImageInfo.iPhysicalPresent = ETrue;

        TInt horzPixelsPerMeter = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
        TInt vertPixelsPerMeter = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);

        if (horzPixelsPerMeter > 0)
            iImageInfo.iPhysicalSize.iWidth = iImageInfo.iSize.iWidth * KTwipsPerMeter / horzPixelsPerMeter;
        if (vertPixelsPerMeter > 0)
            iImageInfo.iPhysicalSize.iHeight = iImageInfo.iSize.iHeight * KTwipsPerMeter / vertPixelsPerMeter;
        }
    }

// Process a PNG tRNS (Transparency) chunk
void CPngReadCodec::DoProcesstRNSL(const TUint8* aDataPtr,TInt aChunkLength)
    {
    iImageInfo.iTransparencyPresent = ETrue;

    if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor) // 3
        {
        if (aChunkLength < 1)
            User::Leave(KErrCorrupt);

        Mem::Copy(iImageInfo.iTransparencyValue,aDataPtr,aChunkLength);
        }
    else if (iImageInfo.iColorType == TPngImageInformation::EGrayscale) // 0
        {
        if (aChunkLength < 2)
            User::Leave(KErrCorrupt);

        iImageInfo.iTransparentGray = TUint16((aDataPtr[0] << 8) | aDataPtr[1]);
        }
    else if (iImageInfo.iColorType == TPngImageInformation::EDirectColor) // 2
        {
        if (aChunkLength < 6)
            User::Leave(KErrCorrupt);

        iImageInfo.iTransparentRed = TUint16((aDataPtr[0] << 8) | aDataPtr[1]);
        iImageInfo.iTransparentGreen = TUint16((aDataPtr[2] << 8) | aDataPtr[3]);
        iImageInfo.iTransparentBlue = TUint16((aDataPtr[4] << 8) | aDataPtr[5]);
        }
    }

// Process a PNG image data
void CPngReadCodec::DoProcessDataL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
    {
    // Data is passed to the decompressor
    TInt bytesToProcess = Min(aDataPtrLimit - aDataPtr,iChunkBytesRemaining);
    iDataDes.Set(aDataPtr,bytesToProcess);
    iDecompressor->SetInput(iDataDes);

    while (iDecompressor->AvailIn() > 0)
        iDecompressor->InflateL();

    aDataPtr += bytesToProcess;
    iChunkBytesRemaining -= bytesToProcess;
    }

// From MEZBufferManager: manage decompressor stream
void CPngReadCodec::InitializeL(CEZZStream& aZStream)
    {
    aZStream.SetOutput(iDecoder->FirstBuffer());
    }

void CPngReadCodec::NeedInputL(CEZZStream& /*aZStream*/)
    {
    }

void CPngReadCodec::NeedOutputL(CEZZStream& aZStream)
    {
    aZStream.SetOutput(iDecoder->DecodeL());
    }

void CPngReadCodec::FinalizeL(CEZZStream& /*aZStream*/)
    {
    iDecoder->DecodeL();
    }



//
// CPngWriteCodec: writes a PNG image
//


CPngWriteCodec::CPngWriteCodec(TInt aBpp, TBool aColor, TBool aPaletted, TInt aCompressionLevel)
    : iCompressionLevel(aCompressionLevel), iCompressorPtr(NULL, 0)
    {
    // Set bpp
    iImageInfo.iBitsPerPixel = aBpp;
    switch (aBpp)
        {
        case 1:
            iImageInfo.iBitDepth = 1;
            break;
        case 2:
            iImageInfo.iBitDepth = 2;
            break;
        case 4:
            iImageInfo.iBitDepth = 4;
            break;
        case 8:
        case 24:
            iImageInfo.iBitDepth = 8;
            break;
        default:
            break;
        }

    // Set color type
    if (aColor && aPaletted)
        iImageInfo.iColorType = TPngImageInformation::EIndexedColor;
    else if (aColor)
        iImageInfo.iColorType = TPngImageInformation::EDirectColor;
    else
        iImageInfo.iColorType = TPngImageInformation::EGrayscale;
    }

CPngWriteCodec::~CPngWriteCodec()
    {
    delete iCompressor;
    delete iEncoder;
    }

// Called by framework at start of conversion operation
void CPngWriteCodec::InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource)
    {
    if (aDst.Length() == 0)
        User::Leave(KErrArgument);  // Not enough length for anything

    SetSource(&aSource);
    iDestStartPtr = const_cast<TUint8*>(aDst.Ptr());
    iDestPtr = iDestStartPtr;
    iDestPtrLimit = iDestPtr + aDst.MaxLength();

    // Set image information
    const SEpocBitmapHeader& header = aSource.Header();
    iImageInfo.iSize = header.iSizeInPixels;

    switch (iImageInfo.iBitDepth)
        {
        case 1:
        case 2:
        case 4:
            if (iImageInfo.iColorType == TPngImageInformation::EDirectColor)
                {
                // Bit depths 1, 2 and 4 don't support RGB colour (color mode 2)
                // Must use paletted colour or greyscale
                User::Leave(KErrNotSupported);
                break;
                }
            // fall through to case 8
        case 8:
            break;
        default:
            User::Leave(KErrNotSupported);  // unsupported bit depth
            break;
        }

    iImageInfo.iCompressionMethod = TPngImageInformation::EDeflateInflate32K;
    iImageInfo.iFilterMethod = TPngImageInformation::EAdaptiveFiltering;
    iImageInfo.iInterlaceMethod = TPngImageInformation::ENoInterlace;

    // Create encoder
    if (iEncoder)
        {
        delete iEncoder;
        iEncoder = NULL;
        }
    iEncoder = CPngWriteSubCodec::NewL(iImageInfo, &aSource);

    // Create compressor
    if (iCompressor)
        {
        delete iCompressor;
        iCompressor = NULL;
        }
    iCompressor = CEZCompressor::NewL(*this, iCompressionLevel);

    // Initial encoder state
    if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor)
        iEncoderState = EPngWritePLTE;    
    else
        iEncoderState = EPngDeflate;
    iCallAgain = ETrue;       // to make sure we call DeflateL

    // Write header
    User::LeaveIfError(WriteHeaderChunk(aDst));
    }

// Called by the framework to process frame data
TFrameState CPngWriteCodec::ProcessFrameL(TBufPtr8& aDst)
    {
    if (aDst.Length() == 0)
        User::Leave(KErrArgument);  // Not enough length for anything

    TFrameState state = EFrameIncomplete;
    iDestStartPtr = const_cast<TUint8*>(aDst.Ptr());
    iDestPtr = iDestStartPtr;
    iDestPtrLimit = iDestPtr + aDst.MaxLength();

    // Set return buffer length to 0 initially
    aDst.SetLength(0);

    while (aDst.Length() == 0 && state != EFrameComplete)
        {
        // Loop round until we have some data to return or
        // the image is encoded
        switch (iEncoderState)
            {
            case EPngWritePLTE:
                WritePLTEChunk(aDst);
                break;
            case EPngDeflate:
                DeflateEncodedDataL(aDst, state);
                break;
            case EPngWriteIDAT:
                WriteIDATChunk(aDst);
                break;
            case EPngFlush:
                FlushCompressedDataL(aDst, state);
                break;
            case EPngEndChunk:
                WriteEndChunk(aDst);
                state = EFrameComplete;
                break;
            default:
                break;
            }
        }

    return state;
    }

// Write a compressed image data chunk
void CPngWriteCodec::DeflateEncodedDataL(TBufPtr8& aDst, TFrameState& /*aState*/)
    {
    // Set ptr for compressed data
    const TInt dataLength = aDst.MaxLength() - KPngChunkLengthSize - KPngChunkIdSize - KPngChunkCRCSize;
    ASSERT(dataLength > 0);
    iCompressorPtr.Set(iDestPtr + KPngChunkIdSize + KPngChunkLengthSize, dataLength, dataLength);

    // Initialise input/output for compressor
    iCompressor->SetInput(iEncoder->EncodeL(iScanline));
    iScanline++;
    iCompressor->SetOutput(iCompressorPtr);

    while ((iEncoderState == EPngDeflate) && iCallAgain)
        iCallAgain = iCompressor->DeflateL();

    // Write the IDAT chunk
    WriteIDATChunk(aDst);
    iEncoderState = EPngFlush;
    }

void CPngWriteCodec::FlushCompressedDataL(TBufPtr8& aDst, TFrameState& /*aState*/)
    {
    if (iCallAgain)
        {
        iCallAgain = iCompressor->DeflateL();
        WriteIDATChunk(aDst);
        }
    else
        {
        iEncoderState = EPngEndChunk;
        }
    }

// Write a PLTE chunk
void CPngWriteCodec::WritePLTEChunk(TBufPtr8& aDst)
    {
    ASSERT(iEncoder->Palette() &&
           (iImageInfo.iColorType == TPngImageInformation::EIndexedColor ||
            iImageInfo.iColorType == TPngImageInformation::EDirectColor ||
            iImageInfo.iColorType == TPngImageInformation::EAlphaDirectColor));   // allowed color types for PLTE chunk

    // Get palette entries
    CPalette* palette = iEncoder->Palette();
    ASSERT(palette);
    const TInt count = palette->Entries();
    TUint8* ptr = iDestPtr + KPngChunkIdSize + KPngChunkLengthSize;
    TInt length = count * 3;
    TPtr8 data(ptr, length, length);
    for (TInt i=0; i < count; i++)
        {
        TRgb rgb = palette->GetEntry(i);
        *ptr = TUint8(rgb.Red());
        ptr++;
        *ptr = TUint8(rgb.Green());
        ptr++;
        *ptr = TUint8(rgb.Blue());
        ptr++;
        }
    // Write PLTE chunk
    WritePngChunk(iDestPtr, KPngPLTEChunkId, data, length);
    ASSERT(length % 3 == 0);    // length must be divisible by 3
    aDst.SetLength(length);
    iEncoderState = EPngDeflate;
    }

// Write a data chunk
void CPngWriteCodec::WriteIDATChunk(TBufPtr8& aDst)
    {
    TPtrC8 ptr(iCompressor->OutputDescriptor());
    if (ptr.Length())
        {
        TInt length = 0;
        WritePngChunk(iDestPtr, KPngIDATChunkId, ptr, length);
        aDst.SetLength(length);

        // New output can write to the same compressor ptr
        iCompressor->SetOutput(iCompressorPtr);
        }

    if (iCallAgain)
        iEncoderState = EPngFlush;
    }

// Write an END chunk
void CPngWriteCodec::WriteEndChunk(TBufPtr8& aDst)
    {
    // Write IEND chunk
    TInt length = 0;
    WritePngChunk(iDestPtr, KPngIENDChunkId, KNullDesC8, length);
    aDst.SetLength(length);
    }

// Write a header chunk
TInt CPngWriteCodec::WriteHeaderChunk(TBufPtr8& aDst)
    {
    // Write signature
    Mem::Copy(iDestPtr, &KPngSignature[0], KPngFileSignatureLength);
    iDestPtr += KPngFileSignatureLength;

    // Write IHDR chunk
    TBuf8<KPngIHDRChunkSize> buffer;
    TUint8* ptr = const_cast<TUint8*>(buffer.Ptr());
    // Set length of data
    buffer.SetLength(KPngIHDRChunkSize);
    // Chunk data
    // width (4 bytes)
    if ((iImageInfo.iSize.iWidth == 0) ||
        (static_cast<TUint>(iImageInfo.iSize.iWidth) > KPngMaxImageSize))
        {
        return KErrArgument;   // invalid width
        }
    PtrWriteUtil::WriteBigEndianInt32(ptr, iImageInfo.iSize.iWidth);
    ptr += 4;
    // height (4 bytes)
    if ((iImageInfo.iSize.iHeight == 0) ||
        (static_cast<TUint>(iImageInfo.iSize.iHeight) > KPngMaxImageSize))
        {
        return KErrArgument;   // invalid height
        }
    PtrWriteUtil::WriteBigEndianInt32(ptr, iImageInfo.iSize.iHeight);
    ptr += 4;
    // bit depth (1 byte)
    PtrWriteUtil::WriteInt8(ptr, iImageInfo.iBitDepth);
    ptr++;
    // colour type (1 byte)
    PtrWriteUtil::WriteInt8(ptr, iImageInfo.iColorType);
    ptr++;
    // compression method (1 byte)
    PtrWriteUtil::WriteInt8(ptr, iImageInfo.iCompressionMethod);
    ptr++;
    // filter method (1 byte)
    PtrWriteUtil::WriteInt8(ptr, iImageInfo.iFilterMethod);
    ptr++;
    // interlace method (1 byte)
    PtrWriteUtil::WriteInt8(ptr, iImageInfo.iInterlaceMethod);
    ptr++;

    TInt length = 0;
    WritePngChunk(iDestPtr, KPngIHDRChunkId, buffer, length);
    aDst.SetLength(KPngFileSignatureLength + length);

    return KErrNone;
    }

// Chunk writing helper function
void CPngWriteCodec::WritePngChunk(TUint8*& aDestPtr, const TDesC8& aChunkId, const TDesC8& aData, TInt& aLength)
    {
    // Chunk length (4 bytes)
    PtrWriteUtil::WriteBigEndianInt32(aDestPtr, aData.Length());
    aDestPtr += KPngChunkLengthSize;
    TUint8* crcPtr = aDestPtr;   // start position for calculating CRC
    // Chunk type (4 bytes)
    Mem::Copy(aDestPtr, aChunkId.Ptr(), KPngChunkIdSize);
    aDestPtr += KPngChunkIdSize;
    // Chunk data (0...n bytes)
    Mem::Copy(aDestPtr, aData.Ptr(), aData.Length());
    aDestPtr += aData.Length();
    // CRC (4 bytes)
    TUint32 crc = KPngCrcMask;
    GetCrc(crc, crcPtr, KPngChunkIdSize + aData.Length());
    crc ^= KPngCrcMask;
    PtrWriteUtil::WriteBigEndianInt32(aDestPtr, crc);
    aDestPtr += KPngChunkCRCSize;
    // Length of chunk
    aLength = KPngChunkLengthSize + KPngChunkIdSize + aData.Length() + KPngChunkCRCSize;
    }

// from MEZBufferManager, manage data compressor
void CPngWriteCodec::InitializeL(CEZZStream& /*aZStream*/)
    {
    }

void CPngWriteCodec::NeedInputL(CEZZStream& aZStream)
    {
    // Give compressor more data from encoder
    aZStream.SetInput(iEncoder->EncodeL(iScanline));
    if (iCompressor->AvailIn() != 0)
        iScanline++;
    }

void CPngWriteCodec::NeedOutputL(CEZZStream& /*aZStream*/)
    {
    // Signal to write an IDAT chunk
    iEncoderState = EPngWriteIDAT;
    }

void CPngWriteCodec::FinalizeL(CEZZStream& /*aZStream*/)
    {
    }

// Calculate CRC for PNG chunks
void CPngWriteCodec::GetCrc(TUint32& aCrc, const TUint8* aPtr, const TInt aLength)
    {
    if (!iCrcTableCalculated)
        CalcCrcTable();
    TUint32 code = aCrc;
    for (TInt i=0; i < aLength; i++)
        code = iCrcTable[(code ^ aPtr[i]) & 0xff] ^ (code >> 8);
    aCrc = code;
    }

void CPngWriteCodec::CalcCrcTable()
    {
    for (TInt i=0; i < KPngCrcTableLength; i++)
        {
        TUint32 code = static_cast<TUint32>(i);

        for (TInt j = 0; j < 8; j++)
            {
            if (code & 1)
                code = 0xedb88320 ^ (code >> 1);
            else
                code = code >> 1;
            }
        iCrcTable[i] = code;
        }
    iCrcTableCalculated = ETrue;
    }

// PNGConvert.CPP
//
// Copyright (c) 1999-2005 Symbian Software Ltd.  All rights reserved.
//

// General BAFL headers
#include <barsc.h>
#include <barsread.h>
#include <bautils.h>
// ICL headers
#include <ImageConversion.h>

#include "ImageUtils.h"
#include <101F4122_extra.rsg>
#include "UIDs.h"
#include "PNGCodec.h"

//
// PNG decoder class
//

// Simple factory function
CPngDecoder* CPngDecoder::NewL()
    {
    return new(ELeave) CPngDecoder;
    }

CPngDecoder::CPngDecoder()
    {
    }

// Destructor calls base class cleanup
CPngDecoder::~CPngDecoder()
    {
    Cleanup();
    }

// Gets the image type: always PNG, no sub type
void CPngDecoder::ImageType(TInt aFrameNumber, TUid& aImageType, TUid& aImageSubType) const
    {
    __ASSERT_ALWAYS(aFrameNumber == 0, Panic(KErrArgument));
    aImageType = KImageTypePNGUid;
    aImageSubType = KNullUid;
    }

// Scans the image header.
void CPngDecoder::ScanDataL()
    {
    // Validate that format is correct
    ReadFormatL();
    ASSERT(ImageReadCodec() == NULL);

    // Create a codec to read the PNG image
    CPngReadCodec* imageReadCodec;
    imageReadCodec = new(ELeave) CPngReadCodec;
    // Let the framework takes ownership of the codec
    SetImageReadCodec(imageReadCodec);
    imageReadCodec->ConstructL();

    // Fill in image information for all frames
    ReadFrameHeadersL();
    }

// Validate that the file is PNG format
void CPngDecoder::ReadFormatL()
    {
    // Read initial data block
    TPtrC8 bufferDes;
    ReadDataL(0, bufferDes, KPngFileSignatureLength);

    // Validate the header.
    if (bufferDes.Length() < KPngFileSignatureLength)
        User::Leave(KErrUnderflow);

    const TUint8* ptr = bufferDes.Ptr();
    if (Mem::Compare(ptr, KPngFileSignatureLength, KPngSignature, KPngFileSignatureLength)!=0)
        User::Leave(KErrCorrupt);

    // Set start position of image data following the header
    SetStartPosition(KPngFileSignatureLength);

    // Set maximum data length as don't know exactly
    SetDataLength(KMaxTInt);
    }

// Gets text descriptions of image properties
CFrameInfoStrings* CPngDecoder::FrameInfoStringsL(RFs& aFs, TInt aFrameNumber)
    {
    const TUid KPngCodecDllUid = {KExPNGCodecDllUidValue};

    // Strings are read from 101F4122_extra.rss
    RResourceFile resourceFile;
    OpenExtraResourceFileLC(aFs,KPngCodecDllUid,resourceFile);
    HBufC8* resourceInfo = resourceFile.AllocReadLC(THEDECODERINFO);
    TResourceReader resourceReader;
    resourceReader.SetBuffer(resourceInfo);

    TBuf<KCodecResourceStringMax> info;
    TBuf<KCodecResourceStringMax> templte;

    const TFrameInfo& frameInfo = FrameInfo(aFrameNumber);
    CFrameInfoStrings* frameInfoStrings = CFrameInfoStrings::NewLC();

    // Set decoder name
    info = resourceReader.ReadTPtrC();
    frameInfoStrings->SetDecoderL(info);
    // Set image format name
    info = resourceReader.ReadTPtrC();
    frameInfoStrings->SetFormatL(info);
    // Set image dimensions
    TInt width = frameInfo.iOverallSizeInPixels.iWidth;
    TInt height = frameInfo.iOverallSizeInPixels.iHeight;
    TInt depth = frameInfo.iBitsPerPixel;
    templte = resourceReader.ReadTPtrC();
    info.Format(templte, width, height);
    frameInfoStrings->SetDimensionsL(info);
    // Set image depth, for colour or b/w as appropriate
    CDesCArrayFlat* resourceArray = resourceReader.ReadDesCArrayL();
    CleanupStack::PushL(resourceArray);
    TUint formatIndex = (frameInfo.iFlags & TFrameInfo::EColor) ? 1 : 0;
    templte = (*resourceArray)[formatIndex];
    CleanupStack::PopAndDestroy(resourceArray);
    info.Format(templte, depth);
    frameInfoStrings->SetDepthL(info);
    // Set image details strings
    info = resourceReader.ReadTPtrC(); // read details, then see if we use it
    if (frameInfo.iFlags & TFrameInfo::EAlphaChannel && frameInfo.iFlags & TFrameInfo::EColor)
        {
        frameInfoStrings->SetDetailsL(info);
        }
    // Cleanup and return
    CleanupStack::Pop(frameInfoStrings); 
    CleanupStack::PopAndDestroy(2); // resourceInfo + resourceFile
    return frameInfoStrings;
    }

//
// PNG encoder class
//

// Simple factory function
CPngEncoder* CPngEncoder::NewL()
    {
    return new(ELeave) CPngEncoder;
    }

CPngEncoder::CPngEncoder()
    {
    }

// Destructor calls base class cleanup
CPngEncoder::~CPngEncoder()
    {
    CImageEncoderPlugin::Cleanup();
    }

// Sets up the codec to encode the frame
void CPngEncoder::PrepareEncoderL(const CFrameImageData* aFrameImageData)
    {
    // Default encode parameters
    TInt bpp = 24;
    TBool color = ETrue;
    TBool paletted = EFalse;
    TInt compressionLevel = TPngEncodeData::EDefaultCompression;

    // Use encode params in aFrameImageData, if present
    const TInt count = (aFrameImageData) ? aFrameImageData->FrameDataCount() : 0;
    for (TInt i=0; i < count; i++)
        {
        const TFrameDataBlock* encoderData = aFrameImageData->GetFrameData(i);
        if (encoderData->DataType() == KPNGEncodeDataUid)
            {
            const TPngEncodeData* pngEncodeData = static_cast<const TPngEncodeData*>(encoderData);
            bpp = pngEncodeData->iBitsPerPixel;
            color = pngEncodeData->iColor;
            paletted = pngEncodeData->iPaletted;
            compressionLevel = pngEncodeData->iLevel;
            }
        }

    // Create the codec to write a PNG image
    CPngWriteCodec* codec = new(ELeave) CPngWriteCodec(bpp, color, paletted, compressionLevel);
    // Let the framework takes ownership of the codec
    SetImageWriteCodec(codec);      
    codec->ConstructL();
    }

/ PNGProxy.cpp
//
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.
//

// Supplies the factory function called by ECom, and factories for
// the PNG decoder and encoder

// Ecom headers
#include <ECom.h>
#include <ImplementationProxy.h>

// ICL base class
#include <ICL\ImageConstruct.h>

#include "UIDS.h"
#include "PNGCodec.h"


//
// Factory for PNG decoder
//
class CPngDecodeConstruct : public CImageDecodeConstruct
    {
public:
    // Constructs the factory
    static CPngDecodeConstruct* NewL();

    // Supplies a new decoder
    // from CImageDecodeConstruct
    CImageDecoderPlugin* NewPluginL() const;
    };

CPngDecodeConstruct* CPngDecodeConstruct::NewL()
    {
    CPngDecodeConstruct* self = new (ELeave) CPngDecodeConstruct;
    return self;
    }

CImageDecoderPlugin* CPngDecodeConstruct::NewPluginL() const
    {
    return CPngDecoder::NewL();
    }


//
// Factory for PNG encoder
//
class CPngEncodeConstruct : public CImageEncodeConstruct
    {
public:
    // Constructs the factory
    static CPngEncodeConstruct* NewL();

    // Supplies a new encoder
    // from CImageEncodeConstruct
    CImageEncoderPlugin* NewPluginL() const;
    };

CPngEncodeConstruct* CPngEncodeConstruct::NewL()
    {
    CPngEncodeConstruct* self = new (ELeave) CPngEncodeConstruct;
    return self;
    }

CImageEncoderPlugin* CPngEncodeConstruct::NewPluginL() const
    {
    return CPngEncoder::NewL();
    }



//
// Exported proxy for ECom instantiation method resolution
//

// Define the factories for the PNG converters
const TImplementationProxy ImplementationTable[] =
    {
        IMPLEMENTATION_PROXY_ENTRY(KExPNGDecoderImplementationUidValue, CPngDecodeConstruct::NewL),
        IMPLEMENTATION_PROXY_ENTRY(KExPNGEncoderImplementationUidValue, CPngEncodeConstruct::NewL)
    };

EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
    {
    aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
    return ImplementationTable;
    }

//
// Global panic function
//
GLDEF_C void Panic(TInt aError)
    {
    _LIT(KPNGPanicCategory, "PNGExConvertPlugin");
    User::Panic(KPNGPanicCategory, aError);
    }

// PNGSCANLINEDECODER.CPP
//
// Copyright (c) 1997-2002 Symbian Ltd.  All rights reserved.
//

#include "PNGCodec.h"

const TInt KPngScanlineFilterTypeLength = 1;

const TInt KColStart[KPngNumInterlacedPasses] = { 0, 4, 0, 2, 0, 1, 0, 0 };
const TInt KRowStart[KPngNumInterlacedPasses] = { 0, 0, 4, 0, 2, 0, 1, 0 };
const TInt KColIncrement[KPngNumInterlacedPasses] = { 8, 8, 4, 4, 2, 2, 1, 0 };
const TInt KRowIncrement[KPngNumInterlacedPasses] = { 8, 8, 8, 4, 4, 2, 2, 0 };
const TInt KBlockWidth[KPngNumInterlacedPasses] = { 8, 4, 4, 2, 2, 1, 1, 0 };
const TInt KBlockHeight[KPngNumInterlacedPasses] = { 8, 8, 4, 4, 2, 2, 1, 0 };

//
// These classes specialise the PNG reader to read
// scanlines with different bitmap depths/colour types
//

class CBitDepth1Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth2Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth4Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth8Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth8ColorType2Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth8ColorType4Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth8ColorType6Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth16ColorType0Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth16ColorType2Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth16ColorType4Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

class CBitDepth16ColorType6Decoder : public CPngReadSubCodec
    {
private:
    virtual void DoConstructL();
    virtual TInt ScanlineBufferSize(TInt aPixelLength);
    virtual void DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit);
    };

//
// CPngReadSubCodec: base class for classes that read scanlines
//

// Factory function
CPngReadSubCodec* CPngReadSubCodec::NewL(CImageProcessor* aImageProc,CImageProcessor* aMaskProc,const TPngImageInformation& aInfo)
    {
    CPngReadSubCodec* self = NULL;

    switch (aInfo.iBitDepth)
        {
    case 1:
        self = new(ELeave) CBitDepth1Decoder;
        break;
    case 2:
        self = new(ELeave) CBitDepth2Decoder;
        break;
    case 4:
        self = new(ELeave) CBitDepth4Decoder;
        break;
    case 8:
        switch (aInfo.iColorType)
            {
        case TPngImageInformation::EGrayscale:
        case TPngImageInformation::EIndexedColor:
            self = new(ELeave) CBitDepth8Decoder;
            break;
        case TPngImageInformation::EDirectColor:
            self = new(ELeave) CBitDepth8ColorType2Decoder;
            break;
        case TPngImageInformation::EAlphaGrayscale:
            self = new(ELeave) CBitDepth8ColorType4Decoder;
            break;
        case TPngImageInformation::EAlphaDirectColor:
            self = new(ELeave) CBitDepth8ColorType6Decoder;
            break;
        default:
            User::Leave(KErrNotSupported);
            break;
            }
        break;
    case 16:
        switch (aInfo.iColorType)
            {
        case TPngImageInformation::EGrayscale:
            self = new(ELeave) CBitDepth16ColorType0Decoder;
            break;
        case TPngImageInformation::EDirectColor:
            self = new(ELeave) CBitDepth16ColorType2Decoder;
            break;
        case TPngImageInformation::EAlphaGrayscale:
            self = new(ELeave) CBitDepth16ColorType4Decoder;
            break;
        case TPngImageInformation::EAlphaDirectColor:
            self = new(ELeave) CBitDepth16ColorType6Decoder;
            break;
        case TPngImageInformation::EIndexedColor:
        default:
            User::Leave(KErrNotSupported);
            break;
            }
        break;
    default:
        User::Leave(KErrNotSupported);
        break;
        }

    CleanupStack::PushL(self);
    self->ConstructL(aImageProc,aMaskProc,aInfo);
    CleanupStack::Pop(); // self
    return self;
    }

CPngReadSubCodec::CPngReadSubCodec():
    iScanlineDes1(NULL,0),
    iScanlineDes2(NULL,0)
    {}

CPngReadSubCodec::~CPngReadSubCodec()
    {
    delete iScanlineBuffer1;
    delete iScanlineBuffer2;
    }

void CPngReadSubCodec::ConstructL(CImageProcessor* aImageProc,CImageProcessor* aMaskProc,const TPngImageInformation& aInfo)
    {
    iImageProc = aImageProc;
    iMaskProc = aMaskProc;
    iInfo = aInfo;
    iScanlineBufferSize = ScanlineBufferSize(iInfo.iSize.iWidth);

    DoConstructL();

    iScanlineBuffer1 = HBufC8::NewMaxL(iScanlineBufferSize);
    iScanlineBuffer2 = HBufC8::NewMaxL(iScanlineBufferSize);

    if (iInfo.iInterlaceMethod == TPngImageInformation::EAdam7Interlace)
        {
        iInterlacedScanlineBufferSize[0] = ScanlineBufferSize((iInfo.iSize.iWidth + 7) >> 3);
        iInterlacedScanlineBufferSize[1] = ScanlineBufferSize((iInfo.iSize.iWidth + 3) >> 3);
        iInterlacedScanlineBufferSize[2] = ScanlineBufferSize((iInfo.iSize.iWidth + 3) >> 2);
        iInterlacedScanlineBufferSize[3] = ScanlineBufferSize((iInfo.iSize.iWidth + 1) >> 2);
        iInterlacedScanlineBufferSize[4] = ScanlineBufferSize((iInfo.iSize.iWidth + 1) >> 1);
        iInterlacedScanlineBufferSize[5] = ScanlineBufferSize(iInfo.iSize.iWidth >> 1);
        iInterlacedScanlineBufferSize[6] = iScanlineBufferSize;
        iInterlacedScanlineBufferSize[7] = 0;
        iPass = 0;

        iScanlineDes1.Set(&(iScanlineBuffer1->Des())[0],iInterlacedScanlineBufferSize[0],iInterlacedScanlineBufferSize[0]);
        iScanlineDes2.Set(&(iScanlineBuffer2->Des())[0],iInterlacedScanlineBufferSize[0],iInterlacedScanlineBufferSize[0]);
        }
    else
        {
        iScanlineDes1.Set(&(iScanlineBuffer1->Des())[0],iScanlineBufferSize,iScanlineBufferSize);
        iScanlineDes2.Set(&(iScanlineBuffer2->Des())[0],iScanlineBufferSize,iScanlineBufferSize);
        }
    }

TDes8& CPngReadSubCodec::FirstBuffer()
    {
    iScanlineDes1.FillZ();
    iCurrentScanlineBuffer = 2;
    return iScanlineDes2;
    }

TDes8& CPngReadSubCodec::DecodeL()
    {
    TUint8* dataPtr = (iCurrentScanlineBuffer == 1) ? &iScanlineDes1[1] : &iScanlineDes2[1];
    const TUint8* dataPtrLimit = dataPtr + iScanlineDes1.Length() - 1;

    FilterScanlineDataL(dataPtr,dataPtrLimit);
    DoDecode(dataPtr,dataPtrLimit);
    UpdatePos();

    if (iCurrentScanlineBuffer == 1)
        {
        iCurrentScanlineBuffer = 2;
        return iScanlineDes2;
        }
    else
        {
        iCurrentScanlineBuffer = 1;
        return iScanlineDes1;
        }
    }

void CPngReadSubCodec::FilterScanlineDataL(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    TInt filterType = (iCurrentScanlineBuffer == 1) ? iScanlineDes1[0] : iScanlineDes2[0];

    switch (filterType)
        {
    case 0: // None
        break;
    case 1: // Sub
        {
        aDataPtr += iBytesPerPixel;

        while (aDataPtr < aDataPtrLimit)
            {
            aDataPtr[0] = TUint8(aDataPtr[0] + aDataPtr[-iBytesPerPixel]);
            aDataPtr++;
            }
        }
        break;
    case 2: // Up
        {
        TUint8* altDataPtr = (iCurrentScanlineBuffer == 1) ? &iScanlineDes2[1] : &iScanlineDes1[1];

        while (aDataPtr < aDataPtrLimit)
            *aDataPtr++ = TUint8(*aDataPtr + *altDataPtr++);
        }
        break;
    case 3: // Average
        {
        const TUint8* tempDataPtrLimit = Min<const TUint8*>(aDataPtr + iBytesPerPixel,aDataPtrLimit);
        TUint8* altDataPtr = (iCurrentScanlineBuffer == 1) ? &iScanlineDes2[1] : &iScanlineDes1[1];

        while (aDataPtr < tempDataPtrLimit)
            {
            aDataPtr[0] = TUint8(aDataPtr[0] + (altDataPtr[0] / 2));
            aDataPtr++;
            altDataPtr++;
            }

        while (aDataPtr < aDataPtrLimit)
            {
            aDataPtr[0] = TUint8(aDataPtr[0] + ((altDataPtr[0] + aDataPtr[-iBytesPerPixel]) / 2));
            aDataPtr++;
            altDataPtr++;
            }
        }
        break;
    case 4: // Paeth
        {
        const TUint8* tempDataPtrLimit = Min<const TUint8*>(aDataPtr + iBytesPerPixel,aDataPtrLimit);
        TUint8* altDataPtr = (iCurrentScanlineBuffer == 1) ? &iScanlineDes2[1] : &iScanlineDes1[1];

        while (aDataPtr < tempDataPtrLimit)
            {
            aDataPtr[0] = TUint8(aDataPtr[0] + altDataPtr[0]);
            aDataPtr++;
            altDataPtr++;
            }

        while (aDataPtr < aDataPtrLimit)
            {
            aDataPtr[0] = TUint8(aDataPtr[0] + PaethPredictor(aDataPtr[-iBytesPerPixel],altDataPtr[0],altDataPtr[-iBytesPerPixel]));
            aDataPtr++;
            altDataPtr++;
            }
        }
        break;
    default: // Error
        User::Leave(KErrCorrupt);
        break;
        }
    }

TInt CPngReadSubCodec::PaethPredictor(TInt aLeft,TInt aAbove,TInt aAboveLeft)
    {
    TInt p = aLeft + aAbove - aAboveLeft;
    TInt pa = Abs(p - aLeft);
    TInt pb = Abs(p - aAbove);
    TInt pc = Abs(p - aAboveLeft);

    if (pa <= pb && pa <= pc)
        return aLeft;
    else if (pb <= pc)
        return aAbove;
    else
        return aAboveLeft;
    }

void CPngReadSubCodec::WritePixel(TRgb aPixelColor)
    {
    if (iInfo.iInterlaceMethod == TPngImageInformation::EAdam7Interlace)
        {
        const TInt width = Min(KBlockWidth[iPass],iInfo.iSize.iWidth - iPos.iX);
        const TInt endY = Min(iPos.iY + KBlockHeight[iPass],iInfo.iSize.iHeight);

        TPoint pos(iPos);

        while (pos.iY < endY)
            {
            iImageProc->SetPos(pos);

            for (TInt col = 0; col < width; col++)
                iImageProc->SetPixel(aPixelColor);

            pos.iY++;
            }

        iPos.iX += KColIncrement[iPass];
        }
    else
        iImageProc->SetPixel(aPixelColor);
    }

void CPngReadSubCodec::WritePixel(TRgb aPixelColor,TUint8 aAlphaValue)
    {
    ASSERT(iMaskProc);

    TRgb maskColor(TRgb::Gray256(aAlphaValue));

    if (iInfo.iInterlaceMethod == TPngImageInformation::EAdam7Interlace)
        {
        iImageProc->SetPos(iPos);
        iMaskProc->SetPos(iPos);
        iImageProc->SetPixel(aPixelColor);
        iMaskProc->SetPixel(maskColor);

        iPos.iX += KColIncrement[iPass];
        }
    else
        {
        iImageProc->SetPixel(aPixelColor);
        iMaskProc->SetPixel(maskColor);
        }
    }

void CPngReadSubCodec::UpdatePos()
    {
    if (iInfo.iInterlaceMethod == TPngImageInformation::EAdam7Interlace)
        {
        ASSERT(iPass <= 7);

        iPos.iX = KColStart[iPass];
        iPos.iY += KRowIncrement[iPass];

        while (iPos.iX >= iInfo.iSize.iWidth || iPos.iY >= iInfo.iSize.iHeight)
            {
            iPass++;
            iPos.iX = KColStart[iPass];
            iPos.iY = KRowStart[iPass];
            iScanlineDes1.Set(&(iScanlineBuffer1->Des())[0],iInterlacedScanlineBufferSize[iPass],iInterlacedScanlineBufferSize[iPass]);
            iScanlineDes2.Set(&(iScanlineBuffer2->Des())[0],iInterlacedScanlineBufferSize[iPass],iInterlacedScanlineBufferSize[iPass]);
            iScanlineDes1.FillZ();
            iScanlineDes2.FillZ();
            }
        }
    }

//
// CBitDepth1Decoder: specialised reader for 1bpp scanlines
//
void CBitDepth1Decoder::DoConstructL()
    {
    if (!(iInfo.iColorType & TPngImageInformation::EPaletteUsed))
        { // Set up palette to be grayscale values
        iInfo.iPalette[0] = KRgbBlack;
        iInfo.iPalette[1] = KRgbWhite;


        if (iInfo.iTransparencyPresent)
            {
            if (iInfo.iTransparentGray <= 1)
                iInfo.iTransparencyValue[iInfo.iTransparentGray] = 0;
            }
        }

    // Replicate values to avoid shifts when decoding
    iInfo.iPalette[2] = iInfo.iPalette[1];
    iInfo.iPalette[4] = iInfo.iPalette[1];
    iInfo.iPalette[8] = iInfo.iPalette[1];
    iInfo.iPalette[16] = iInfo.iPalette[1];
    iInfo.iPalette[32] = iInfo.iPalette[1];
    iInfo.iPalette[64] = iInfo.iPalette[1];
    iInfo.iPalette[128] = iInfo.iPalette[1];

    if (iInfo.iTransparencyPresent && iInfo.iTransparencyValue[1] != 255)
        {
        iInfo.iTransparencyValue[2] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[4] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[8] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[16] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[32] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[64] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[128] = iInfo.iTransparencyValue[1];
        }

    iBytesPerPixel = 1;
    if (iInfo.iInterlaceMethod == TPngImageInformation::ENoInterlace)
        {
        TInt pixelPadding = ((iInfo.iSize.iWidth + 7) & ~7) - iInfo.iSize.iWidth;
        iImageProc->SetPixelPadding(pixelPadding);
        if (iMaskProc)
            iMaskProc->SetPixelPadding(pixelPadding);
        }
    }

TInt CBitDepth1Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return ((aPixelLength + 7) / 8) + KPngScanlineFilterTypeLength;
    }

void CBitDepth1Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt dataValue = *aDataPtr++;

            for (TUint mask=0x80; mask!=0; mask>>=1) // iterate with 0x80, 0x40 .. 0x01
                WritePixel(iInfo.iPalette[dataValue & mask],iInfo.iTransparencyValue[dataValue & mask]);
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt dataValue = *aDataPtr++;

            for (TUint mask=0x80; mask!=0; mask>>=1) // iterate with 0x80, 0x40 .. 0x01
                WritePixel(iInfo.iPalette[dataValue & mask]);
            }
        }
    }


//
// CBitDepth2Decoder: specialised reader for 2bpp scanlines
//
void CBitDepth2Decoder::DoConstructL()
    {
    if (!(iInfo.iColorType & TPngImageInformation::EPaletteUsed))
        { // Set up palette to be grayscale values
        iInfo.iPalette[0] = KRgbBlack;
        iInfo.iPalette[1] = KRgbDarkGray;
        iInfo.iPalette[2] = KRgbGray;
        iInfo.iPalette[3] = KRgbWhite;

        if (iInfo.iTransparencyPresent)
            {
            if (iInfo.iTransparentGray <= 3)
                iInfo.iTransparencyValue[iInfo.iTransparentGray] = 0;
            }
        }

    // Replicate values to avoid shifts when decoding
    iInfo.iPalette[4] = iInfo.iPalette[1];
    iInfo.iPalette[8] = iInfo.iPalette[2];
    iInfo.iPalette[12] = iInfo.iPalette[3];

    iInfo.iPalette[16] = iInfo.iPalette[1];
    iInfo.iPalette[32] = iInfo.iPalette[2];
    iInfo.iPalette[48] = iInfo.iPalette[3];

    iInfo.iPalette[64] = iInfo.iPalette[1];
    iInfo.iPalette[128] = iInfo.iPalette[2];
    iInfo.iPalette[192] = iInfo.iPalette[3];

    if (iInfo.iTransparencyPresent)
        {
        iInfo.iTransparencyValue[4] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[8] = iInfo.iTransparencyValue[2];
        iInfo.iTransparencyValue[12] = iInfo.iTransparencyValue[3];

        iInfo.iTransparencyValue[16] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[32] = iInfo.iTransparencyValue[2];
        iInfo.iTransparencyValue[48] = iInfo.iTransparencyValue[3];

        iInfo.iTransparencyValue[64] = iInfo.iTransparencyValue[1];
        iInfo.iTransparencyValue[128] = iInfo.iTransparencyValue[2];
        iInfo.iTransparencyValue[192] = iInfo.iTransparencyValue[3];
        }

    iBytesPerPixel = 1;
    if (iInfo.iInterlaceMethod == TPngImageInformation::ENoInterlace)
        {
        TInt pixelPadding = ((iInfo.iSize.iWidth + 3) & ~3) - iInfo.iSize.iWidth;
        iImageProc->SetPixelPadding(pixelPadding);
        if (iMaskProc)
            iMaskProc->SetPixelPadding(pixelPadding);
        }
    }

TInt CBitDepth2Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return ((aPixelLength + 3) / 4) + KPngScanlineFilterTypeLength;
    }

void CBitDepth2Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt dataValue = *aDataPtr++;

            for (TInt mask=0xc0; mask!=0; mask>>=2) // iterate through 0xc0, 0x30, 0x0c and 0x03
                WritePixel(iInfo.iPalette[dataValue & mask],iInfo.iTransparencyValue[dataValue & mask]);
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt dataValue = *aDataPtr++;

            for (TInt mask=0xc0; mask!=0; mask>>=2) // iterate through 0xc0, 0x30, 0x0c and 0x03
                WritePixel(iInfo.iPalette[dataValue & mask]);
            }
        }
    }


//
// CBitDepth4Decoder: specialised reader for 4bpp scanlines
//

void CBitDepth4Decoder::DoConstructL()
    {
    if (!(iInfo.iColorType & TPngImageInformation::EPaletteUsed))
        { // Set up palette to be grayscale values
        for (TInt index = 0; index < 16; index++)
            iInfo.iPalette[index] = TRgb::Gray16(index);

        if (iInfo.iTransparencyPresent)
            {
            if (iInfo.iTransparentGray <= 15)
                iInfo.iTransparencyValue[iInfo.iTransparentGray] = 0;
            }
        }

    iBytesPerPixel = 1;
    if (iInfo.iInterlaceMethod == TPngImageInformation::ENoInterlace)
        {
        TInt pixelPadding = ((iInfo.iSize.iWidth + 1) & ~1) - iInfo.iSize.iWidth;
        iImageProc->SetPixelPadding(pixelPadding);
        if (iMaskProc)
            iMaskProc->SetPixelPadding(pixelPadding);
        }
    }

TInt CBitDepth4Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return ((aPixelLength + 1) / 2) + KPngScanlineFilterTypeLength;
    }

void CBitDepth4Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt dataValue = *aDataPtr++;

            WritePixel(iInfo.iPalette[dataValue >> 4],iInfo.iTransparencyValue[dataValue >> 4]);
            WritePixel(iInfo.iPalette[dataValue & 0x0f],iInfo.iTransparencyValue[dataValue & 0x0f]);
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt dataValue = *aDataPtr++;

            WritePixel(iInfo.iPalette[dataValue >> 4]);
            WritePixel(iInfo.iPalette[dataValue & 0x0f]);
            }
        }
    }


//
// CBitDepth8Decoder: specialised reader for 8bpp scanlines
//

void CBitDepth8Decoder::DoConstructL()
    {
    if (!(iInfo.iColorType & TPngImageInformation::EPaletteUsed))
        { // Set up palette to be grayscale values
        for (TInt index = 0; index < 256; index++)
            iInfo.iPalette[index] = TRgb::Gray256(index);

        if (iInfo.iTransparencyPresent)
            {
            if (iInfo.iTransparentGray <= 255)
                iInfo.iTransparencyValue[iInfo.iTransparentGray] = 0;
            }
        }

    iBytesPerPixel = 1;
    }

TInt CBitDepth8Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return aPixelLength + KPngScanlineFilterTypeLength;
    }

void CBitDepth8Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(iInfo.iPalette[aDataPtr[0]],iInfo.iTransparencyValue[aDataPtr[0]]);
            aDataPtr++;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            WritePixel(iInfo.iPalette[*aDataPtr++]);
        }
    }


//
// CBitDepth8ColorType2Decoder: specialised reader for 8bpp scanlines, direct colour
//

void CBitDepth8ColorType2Decoder::DoConstructL()
    {
    iBytesPerPixel = 3;
    }

TInt CBitDepth8ColorType2Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 3) + KPngScanlineFilterTypeLength;
    }

void CBitDepth8ColorType2Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt red = aDataPtr[0];
            TInt green = aDataPtr[1];
            TInt blue = aDataPtr[2];
            TRgb pixelColor(red,green,blue);
            if (red == iInfo.iTransparentRed && green == iInfo.iTransparentGreen && blue == iInfo.iTransparentBlue)
                WritePixel(pixelColor,0);
            else
                WritePixel(pixelColor,255);
            aDataPtr += 3;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]));
            aDataPtr += 3;
            }
        }
    }


//
// CBitDepth8ColorType4Decoder: specialised reader for 8bpp scanlines, alpha greyscale
//

void CBitDepth8ColorType4Decoder::DoConstructL()
    {
    iBytesPerPixel = 2;
    }

TInt CBitDepth8ColorType4Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 2) + KPngScanlineFilterTypeLength;
    }

void CBitDepth8ColorType4Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb::Gray256(aDataPtr[0]),aDataPtr[1]);
            aDataPtr += 2;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb::Gray256(aDataPtr[0]));
            aDataPtr += 2;
            }
        }
    }


//
// CBitDepth8ColorType4Decoder: specialised reader for 8bpp scanlines, alpha colour
//

void CBitDepth8ColorType6Decoder::DoConstructL()
    {
    iBytesPerPixel = 4;
    }

TInt CBitDepth8ColorType6Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 4) + KPngScanlineFilterTypeLength;
    }

void CBitDepth8ColorType6Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]),aDataPtr[3]);
            aDataPtr += 4;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]));
            aDataPtr += 4;
            }
        }
    }


//
// CBitDepth16ColorType0Decoder: specialised reader for 16bpp scanlines, greyscale
//

void CBitDepth16ColorType0Decoder::DoConstructL()
    {
    iBytesPerPixel = 2;
    }

TInt CBitDepth16ColorType0Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 2) + KPngScanlineFilterTypeLength;
    }

void CBitDepth16ColorType0Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt gray = (aDataPtr[0] << 8) | aDataPtr[1];
            TRgb pixelColor(TRgb::Gray256(aDataPtr[0]));
            if (gray == iInfo.iTransparentGray)
                WritePixel(pixelColor,0);
            else
                WritePixel(pixelColor,255);
            aDataPtr += 2;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb::Gray256(aDataPtr[0]));
            aDataPtr += 2;
            }
        }
    }


//
// CBitDepth16ColorType2Decoder: specialised reader for 16bpp scanlines, RGB colour
//

void CBitDepth16ColorType2Decoder::DoConstructL()
    {
    iBytesPerPixel = 6;
    }

TInt CBitDepth16ColorType2Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 6) + KPngScanlineFilterTypeLength;
    }

void CBitDepth16ColorType2Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc && iInfo.iTransparencyPresent)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            TInt red = (aDataPtr[0] << 8) | aDataPtr[1];
            TInt green = (aDataPtr[2] << 8) | aDataPtr[3];
            TInt blue = (aDataPtr[4] << 8) | aDataPtr[5];
            TRgb pixelColor(aDataPtr[0],aDataPtr[2],aDataPtr[4]);
            if (red == iInfo.iTransparentRed && green == iInfo.iTransparentGreen && blue == iInfo.iTransparentBlue)
                WritePixel(pixelColor,0);
            else
                WritePixel(pixelColor,255);
            aDataPtr += 6;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb(aDataPtr[0],aDataPtr[2],aDataPtr[4]));
            aDataPtr += 6;
            }
        }
    }


//
// CBitDepth16ColorType4Decoder: specialised reader for 16bpp scanlines, alpha greyscale
//

void CBitDepth16ColorType4Decoder::DoConstructL()
    {
    iBytesPerPixel = 4;
    }

TInt CBitDepth16ColorType4Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 4) + KPngScanlineFilterTypeLength;
    }

void CBitDepth16ColorType4Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb::Gray256(aDataPtr[0]),aDataPtr[2]);
            aDataPtr += 4;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb::Gray256(aDataPtr[0]));
            aDataPtr += 4;
            }
        }
    }


//
// CBitDepth16ColorType6Decoder: specialised reader for 16bpp scanlines, alpha colour
//

void CBitDepth16ColorType6Decoder::DoConstructL()
    {
    iBytesPerPixel = 8;
    }

TInt CBitDepth16ColorType6Decoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * 8) + KPngScanlineFilterTypeLength;
    }

void CBitDepth16ColorType6Decoder::DoDecode(TUint8* aDataPtr,const TUint8* aDataPtrLimit)
    {
    if (iMaskProc)
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb(aDataPtr[0],aDataPtr[2],aDataPtr[4]),aDataPtr[6]);
            aDataPtr += 8;
            }
        }
    else
        {
        while (aDataPtr < aDataPtrLimit)
            {
            WritePixel(TRgb(aDataPtr[0],aDataPtr[2],aDataPtr[4]));
            aDataPtr += 8;
            }
        }
    }

/ PngScanlineEncoder.cpp
//
// Copyright (c) 1997-2002 Symbian Ltd. All rights reserved.
//

#include "PngScanlineEncoder.h"

//
// CPngWriteSubCodec: base class for writing PNG scanlines
//

// Factory function
CPngWriteSubCodec* CPngWriteSubCodec::NewL(const TPngImageInformation& aInfo, const CFbsBitmap* aSource)
    {
    CPngWriteSubCodec* self = NULL;

    switch (aInfo.iBitDepth)
        {
        case 1:
            self = new(ELeave) CBitDepth1Encoder;
            break;
        case 2:
            self = new(ELeave) CBitDepth2Encoder;
            break;
        case 4:
            self = new(ELeave) CBitDepth4Encoder;
            break;
        case 8:
            switch (aInfo.iColorType)
                {
                case TPngImageInformation::EGrayscale:
                case TPngImageInformation::EIndexedColor:
                    self = new(ELeave) CBitDepth8Encoder;
                    break;
                case TPngImageInformation::EDirectColor:
                    self = new(ELeave) CBitDepth8ColorType2Encoder;
                    break;
                default:
                    User::Leave(KErrNotSupported);
                    break;
                }
            break;
        default:
            User::Leave(KErrNotSupported);
            break;
        }
    ASSERT(self);
    CleanupStack::PushL(self);
    self->ConstructL(aInfo, aSource);
    CleanupStack::Pop(self);
    return self;
    }

CPngWriteSubCodec::CPngWriteSubCodec()
    : iScanlineDes(NULL, 0)
    {
    }

CPngWriteSubCodec::~CPngWriteSubCodec()
    {
    delete iScanlineBuffer;
    delete iPalette;
    }

void CPngWriteSubCodec::ConstructL(const TPngImageInformation& aInfo, const CFbsBitmap* aSource)
    {
    iInfo = aInfo;
    iSource = aSource;
    iScanlineBufferSize = ScanlineBufferSize(iInfo.iSize.iWidth);
    iScanlineBuffer = HBufC8::NewMaxL(iScanlineBufferSize);
    iScanlineDes.Set(&(iScanlineBuffer->Des())[0], iScanlineBufferSize, iScanlineBufferSize);

    // Init stuff specific to derived class
    DoConstructL();
    }

void CPngWriteSubCodec::DoConstructL()
    {
    }

TDes8& CPngWriteSubCodec::Buffer()
    {
    iScanlineDes.FillZ();
    return iScanlineDes;
    }

TDes8& CPngWriteSubCodec::EncodeL(const TInt aScanline)
    {
    if (aScanline < iInfo.iSize.iHeight)
        {
        TUint8* dataPtr = const_cast<TUint8*>(iScanlineDes.Ptr());
        const TUint8* dataPtrLimit = dataPtr + iScanlineBufferSize;

        DoEncode(iSource, aScanline, dataPtr, dataPtrLimit);
        }
    else
        {
        iScanlineDes.Set(NULL, 0, 0);
        }
    return iScanlineDes;
    }

TUint8 CPngWriteSubCodec::ReverseBits(const TUint8 aValue) const
    {
    TUint value = aValue;
    TUint reverseVal = 0;
    for (TInt i = 0; i < 8; i++)
        {
        reverseVal <<= 1;
        reverseVal |= value & 1;
        value >>= 1;
        }
    return TUint8(reverseVal);
    }

void CPngWriteSubCodec::EncodePalettedScanline(TUint8* aDataPtr, const CFbsBitmap* aSource, const TInt aScanline,
                                               const TInt aPixelsPerByte, const TInt aShiftValue)
    {
    // Encode a single scanline with indexes into the current palette
    ASSERT(iInfo.iPalettePresent);
    TPoint pos(0, aScanline);
    const TInt scanLength = iInfo.iSize.iWidth;
    for (TInt i=0; i < scanLength; i += aPixelsPerByte)
        {
        // Pack each byte with 'aPixelsPerByte' index values
        TUint8 pixels = 0;
        for (TInt j=0; j < aPixelsPerByte; j++)
            {
            pixels <<= aShiftValue;
            TRgb rgb;
            aSource->GetPixel(rgb, pos);
            pixels |= TUint8(iPalette->NearestIndex(rgb));
            pos.iX++;
            }
        *aDataPtr = pixels;
        aDataPtr++;
        }
    }

//
// These classes specialise the PNG writer to write
// scanlines with different bitmap depths/colour types
//

//
// CBitDepth1Encoder
//

void CBitDepth1Encoder::DoConstructL()
    {
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Setup palette
        iPalette = CPalette::NewL(2);
        iPalette->SetEntry(0, KRgbBlack);
        iPalette->SetEntry(1, KRgbWhite);
        iInfo.iPalettePresent = ETrue;
        }
    }

TInt CBitDepth1Encoder::ScanlineBufferSize(TInt aPixelLength)
    {
    // 8 pixels per byte
    return ((aPixelLength + KPngDepth1RoundUpValue) / KPngDepth1PixelsPerByte) + KPngScanlineFilterTypeLength;
    }

void CBitDepth1Encoder::DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                                 TUint8* aDataPtr, const TUint8* aDataPtrLimit)
    {
    // Filter method
    PtrWriteUtil::WriteInt8(aDataPtr, iInfo.iFilterMethod);
    aDataPtr++;

    // Pixel data
    const TInt scanLength = iInfo.iSize.iWidth;
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Write palette indexes
        EncodePalettedScanline(aDataPtr, aSource, aScanline, KPngDepth1PixelsPerByte, KPngDepth1ShiftValue);
        }
    else
        {
        // Write RGB data
        TInt dataLength = (scanLength + KPngDepth1RoundUpValue) / KPngDepth1PixelsPerByte;
        TPtr8 dataPtr(aDataPtr, dataLength, dataLength);

        aSource->GetScanLine(dataPtr, TPoint(0, aScanline), scanLength, EGray2);

        // Reverse the order of the bits
        while (aDataPtr < aDataPtrLimit)
            {
            aDataPtr[0] = ReverseBits(aDataPtr[0]);
            aDataPtr++;
            }
        }
    }

//
// CBitDepth2Encoder
//

void CBitDepth2Encoder::DoConstructL()
    {
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Setup palette entries
        iPalette = CPalette::NewL(4);
        iPalette->SetEntry(0, KRgbBlack);
        iPalette->SetEntry(1, KRgbDarkGray);
        iPalette->SetEntry(2, KRgbGray);
        iPalette->SetEntry(3, KRgbWhite);
        iInfo.iPalettePresent = ETrue;
        }
    }

TInt CBitDepth2Encoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return ((aPixelLength + KPngDepth2RoundUpValue) / KPngDepth2PixelsPerByte) + KPngScanlineFilterTypeLength;
    }

void CBitDepth2Encoder::DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                                 TUint8* aDataPtr, const TUint8* aDataPtrLimit)
    {
    // Filter method
    PtrWriteUtil::WriteInt8(aDataPtr, iInfo.iFilterMethod);
    aDataPtr++;

    // Pixel data
    const TInt scanLength = iInfo.iSize.iWidth;
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Write palette indexes
        EncodePalettedScanline(aDataPtr, aSource, aScanline, KPngDepth2PixelsPerByte, KPngDepth2ShiftValue);
        }
    else
        {
        // RGB values
        TPtr8 dataPtr(aDataPtr, (scanLength + KPngDepth2RoundUpValue) / KPngDepth2PixelsPerByte);
        aSource->GetScanLine(dataPtr, TPoint(0, aScanline), scanLength, EGray4);

        // Reverse the order of the bits
        while (aDataPtr < aDataPtrLimit)
            {
            TUint8 value = aDataPtr[0];
            TUint8 reverse = 0;
            for (TInt i=0; i < KPngDepth2PixelsPerByte; i++)
                {
                reverse <<= 2;  // advance the bits for the reverse value
                reverse |= value & 0x3; // mask off the 2 bits, then OR with existing reverse value
                value >>= 2;    // advance the bits for the actual value
                }
            aDataPtr[0] = reverse;
            aDataPtr++;
            }
        }
    }

//
// CBitDepth4Encoder
//

void CBitDepth4Encoder::DoConstructL()
    {
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Calculate palette for image
        iPalette = CPalette::NewDefaultL(EColor16);
        iInfo.iPalettePresent = ETrue;
        }
    }

TInt CBitDepth4Encoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return ((aPixelLength + KPngDepth4RoundUpValue) / KPngDepth4PixelsPerByte) + KPngScanlineFilterTypeLength;
    }

void CBitDepth4Encoder::DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                                 TUint8* aDataPtr, const TUint8* aDataPtrLimit)
    {
    // Filter method
    PtrWriteUtil::WriteInt8(aDataPtr, iInfo.iFilterMethod);
    aDataPtr++;

    // Pixel data
    const TInt scanLength = iInfo.iSize.iWidth;
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Write palette indexes
        EncodePalettedScanline(aDataPtr, aSource, aScanline, KPngDepth4PixelsPerByte, KPngDepth4ShiftValue);
        }
    else
        {
        TPtr8 dataPtr(aDataPtr, (scanLength + KPngDepth4RoundUpValue) / KPngDepth4PixelsPerByte);
        aSource->GetScanLine(dataPtr, TPoint(0, aScanline), scanLength,
            (iInfo.iColorType == TPngImageInformation::EDirectColor) ? EColor16 : EGray16);

        // Swap order of the low/high bits
        while (aDataPtr < aDataPtrLimit)
            {
            TUint value = aDataPtr[0];
            TUint low = value << KPngDepth4ShiftValue;
            TUint high = value >> KPngDepth4ShiftValue;
            aDataPtr[0] = TUint8(low | high);
            aDataPtr++;
            }
        }
    }

//
// CBitDepth8ColorType2Encoder
//

TInt CBitDepth8ColorType2Encoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return (aPixelLength * KPngDepth8RgbBytesPerPixel) + KPngScanlineFilterTypeLength;
    }

void CBitDepth8ColorType2Encoder::DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                                           TUint8* aDataPtr, const TUint8* aDataPtrLimit)
    {
    // Filter method
    PtrWriteUtil::WriteInt8(aDataPtr, iInfo.iFilterMethod);
    aDataPtr++;

    // Pixel data
    TPtr8 dataPtr(aDataPtr, iInfo.iSize.iWidth * KPngDepth8RgbBytesPerPixel);
    aSource->GetScanLine(dataPtr, TPoint(0, aScanline), iInfo.iSize.iWidth, EColor16M);

    while (aDataPtr < aDataPtrLimit)
        {
        // Swap the red and blue components of the image data
        TUint8 temp = aDataPtr[0];   // temp = Red
        aDataPtr[0] = aDataPtr[2];    // Red = Blue
        aDataPtr[2] = temp;           // Blue = temp
        aDataPtr += KPngDepth8RgbBytesPerPixel;
        }
    }

//
// CBitDepth8Encoder
//

void CBitDepth8Encoder::DoConstructL()
    {
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Calculate palette for image
        iPalette = CPalette::NewDefaultL(EColor256);
        iInfo.iPalettePresent = ETrue;
        }
    }

TInt CBitDepth8Encoder::ScanlineBufferSize(TInt aPixelLength)
    {
    return aPixelLength + KPngScanlineFilterTypeLength;
    }

void CBitDepth8Encoder::DoEncode(const CFbsBitmap* aSource, const TInt aScanline,
                                 TUint8* aDataPtr, const TUint8* /*aDataPtrLimit*/)
    {
    // Filter method
    PtrWriteUtil::WriteInt8(aDataPtr, iInfo.iFilterMethod);
    aDataPtr++;

    const TInt scanLength = iInfo.iSize.iWidth;
    if (iInfo.iColorType == TPngImageInformation::EIndexedColor)
        {
        // Write palette indexes
        EncodePalettedScanline(aDataPtr, aSource, aScanline, KPngDepth8PixelsPerByte, KPngDepth8ShiftValue);
        }
    else
        {
        // Pixel data
        TPtr8 dataPtr(aDataPtr, scanLength);
        aSource->GetScanLine(dataPtr, TPoint(0, aScanline), scanLength, EGray256);
        }
    }

bld.inf

PRJ_MMPFILES
ExPNGCodec.mmp

// PNGCodec.mmp
//
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.
//

TARGET      ExPNGCodec.dll
TARGETTYPE  PLUGIN

// Ecom Dll recognition UID followed by the unique UID for this dll
UID 0x10009D8D 0x101F4122
VENDORID 0x70000001
CAPABILITY ALL -TCB

SYSTEMINCLUDE   \epoc32\include
SYSTEMINCLUDE   \epoc32\include\ECom
SYSTEMINCLUDE   \epoc32\include\icl


SOURCEPATH  .
SOURCE      PNGCodec.cpp
SOURCE      PNGConvert.cpp
SOURCE      PNGScanlineDecoder.cpp
SOURCE      PNGScanlineEncoder.cpp
SOURCE      PNGProxy.cpp

START RESOURCE 101F4122.RSS
TARGETPATH     \resource\Plugins
HEADER
END

START RESOURCE 101F4122_extra.RSS
TARGETPATH     \resource\icl
HEADER
END

LIBRARY euser.lib
LIBRARY gdi.lib
LIBRARY fbscli.lib
LIBRARY ezlib.lib
LIBRARY bafl.lib
LIBRARY ImageConversion.lib

// 101F4122.rss
//
// Copyright (c) 2001-2002 Symbian Ltd.  All rights reserved.
//
// Registry file for the example PNG encoder/decoder implementation
//

#include <RegistryInfo.rh>
#include "uids.h"

RESOURCE REGISTRY_INFO theInfo
    {
    // UID for the DLL providing the encoder/decoder -- matches that in the .mmp file
    dll_uid = KExPNGCodecDllUidValue;
    interfaces = 
        {
        // Decoder ECom registry information
        INTERFACE_INFO
            {
            // Standard UID of the ICL decoder interface
            interface_uid = 0x101F45AD;
            implementations = 
                {
                // Single implementation of the decoder interface in this DLL
                BINARY_IMPLEMENTATION_INFO
                    {
                    // UID of this specific decoder implementation
                    implementation_uid = KExPNGDecoderImplementationUidValue;
                    version_no = 1;
                    display_name = "EXPNG";
                    // Default data is binary data that can be matched against image header
                    default_data = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
                    /* Opaque data defines:
                        Version (1 byte)
                        Image type Uid (4 bytes)
                        Image sub-type Uid (4 bytes)
                        Number of extensions (1 byte)
                        { ".XXX" \r } (x number extensions)
                        Number mime types (1 byte)
                        [ "mimetype" \r ] (for each mimetype) */
                    opaque_data = {0, 0x10, 0x1F, 0x45, 0xC9, 0, 0, 0, 0, 1, 0x2E, 0x70, 0x6E, 0x67, 0x0D, 1, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x0D};
                    // i.e. 0 0x101F45C9 0x00000000 1 .png 1 image/png
                    }
                };
            },
        // Encoder ECom registry information
        INTERFACE_INFO
            {
            // Standard UID of the ICL encoder interface
            interface_uid = 0x101F45B4;
            implementations = 
                {
                // Single implementation of the decoder interface in this DLL
                BINARY_IMPLEMENTATION_INFO
                    {
                    // UID of this specific encoder implementation
                    implementation_uid = KExPNGEncoderImplementationUidValue;
                    version_no = 1;
                    display_name = "EXPNG";
                    default_data = {0x3F}; // "?" - Won't match
                    opaque_data = {0, 0x10, 0x1F, 0x45, 0xC9, 0, 0, 0, 0, 1, 0x2E, 0x70, 0x6E, 0x67, 0x0D, 1, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x0D};
                    // 0 0x101F45C9 0x00000000 1 .png 1 image/png
                    }
                };
            }
        };
    }

// 101F45C7_extra.rss
//
// Copyright (c) 2001-2002 Symbian Ltd.  All rights reserved.
//
// Extra resource file for the PNG Bitmap Decoder Implementation
//

STRUCT DECODER_INFO
    {
    LTEXT   decoder_name;
    LTEXT   image_format;
    LTEXT   image_dimensions;
    LTEXT   image_depth[];
    LTEXT   image_details;
    }

RESOURCE DECODER_INFO theDecoderInfo
    {
    decoder_name = "Decoder: Example Png decoder V1.0";
    image_format = "Format: Png";
    image_dimensions = "Dimensions: %dx%d pixels";
    image_depth = {"Depth: %dbpp b&w", "Depth: %dbpp colour"};
    image_details = "Details: Image includes alpha channel";
    }


Description

ICLCodec demonstrates how to implement encoder and decoder plug-ins for the Image Converter Library. A decoder converts an image in a particular format into a CFbsBitmap object; an encoder does the reverse. In the example, the Portable Network Graphics (PNG) format is handled.

The program is an ECom plug-in that implements:

The PNG format allows data to be stored in a number of different scanline formats (colour/grey-scale depths), so specialist classes are provided to read and write these formats. Scanline reader classes are derived from CPngReadSubCodec; writer classes from CPngWriteSubCodec.