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";
}
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 decoder plug-in interface CImageDecoderPlugin
by
the class CPngDecoder
the decoder data reader interface CImageReadCodec
by
the class CPngReadCodec
the encoder plug-in interface CImageEncoderPlugin
by
the class CPngEncoder
the encoder data writer interface CImageWriteCodec
by
the class CPngWriteCodec
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
.