/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <vcl/pngwrite.hxx>

#include <cmath>
#include <limits>
#include <rtl/crc.h>
#include <rtl/memory.h>
#include <rtl/alloc.h>
#include <tools/zcodec.hxx>
#include <tools/stream.hxx>
#include <vcl/bmpacc.hxx>
#include <vcl/svapp.hxx>
#include <vcl/alpha.hxx>
#include <osl/endian.h>

// -----------
// - Defines -
// -----------

#define PNG_DEF_COMPRESSION 6

#define PNGCHUNK_IHDR 0x49484452
#define PNGCHUNK_PLTE 0x504c5445
#define PNGCHUNK_IDAT 0x49444154
#define PNGCHUNK_IEND 0x49454e44
#define PNGCHUNK_bKGD 0x624b4744
#define PNGCHUNK_cHRM 0x6348524d
#define PNGCHUNK_gAMA 0x67414d41
#define PNGCHUNK_hIST 0x68495354
#define PNGCHUNK_pHYs 0x70485973
#define PNGCHUNK_sBIT 0x73425420
#define PNGCHUNK_tIME 0x74494d45
#define PNGCHUNK_tEXt 0x74455874
#define PNGCHUNK_tRNS 0x74524e53
#define PNGCHUNK_zTXt 0x7a545874

namespace vcl
{
// -----------------
// - PNGWriterImplImpl -
// -----------------

class PNGWriterImpl
{
public:

				PNGWriterImpl( const BitmapEx& BmpEx,
					const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData = NULL );
				~PNGWriterImpl();

	sal_Bool	Write( SvStream& rOStm );

	std::vector< vcl::PNGWriter::ChunkData >&	GetChunks();

private:

	std::vector< vcl::PNGWriter::ChunkData >	maChunkSeq;

	sal_Int32			mnCompLevel;
	sal_Int32			mnInterlaced;
	sal_uInt32			mnMaxChunkSize;
	sal_Bool				mbStatus;

	BitmapReadAccess*	mpAccess;
	BitmapReadAccess*	mpMaskAccess;
	ZCodec*				mpZCodec;

	sal_uInt8*				mpDeflateInBuf;			// as big as the size of a scanline + alphachannel + 1
	sal_uInt8*				mpPreviousScan;			// as big as mpDeflateInBuf
	sal_uInt8*				mpCurrentScan;
	sal_uLong				mnDeflateInSize;

	sal_uLong				mnWidth, mnHeight;
	sal_uInt8				mnBitsPerPixel;
	sal_uInt8				mnFilterType;			// 0 oder 4;
	sal_uLong				mnBBP;					// bytes per pixel ( needed for filtering )
	sal_Bool				mbTrueAlpha;
	sal_uLong				mnCRC;
	long				mnChunkDatSize;
	sal_uLong				mnLastPercent;

	void				ImplWritepHYs( const BitmapEx& rBitmapEx );
	void				ImplWriteIDAT();
	sal_uLong				ImplGetFilter( sal_uLong nY, sal_uLong nXStart=0, sal_uLong nXAdd=1 );
	void				ImplClearFirstScanline();
	void				ImplWriteTransparent();
	sal_Bool				ImplWriteHeader();
	void				ImplWritePalette();
	void				ImplOpenChunk( sal_uLong nChunkType );
	void				ImplWriteChunk( sal_uInt8 nNumb );
	void				ImplWriteChunk( sal_uInt32 nNumb );
	void				ImplWriteChunk( unsigned char* pSource, sal_uInt32 nDatSize );
	void				ImplCloseChunk( void );
};

// ------------------------------------------------------------------------

PNGWriterImpl::PNGWriterImpl( const BitmapEx& rBmpEx,
	const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) :
		mnCompLevel		( PNG_DEF_COMPRESSION ),
		mbStatus		( sal_True ),
		mpAccess		( NULL ),
		mpMaskAccess	( NULL ),
		mpZCodec		( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ),
        mnCRC(0UL),
		mnLastPercent	( 0UL )
{
	if ( !rBmpEx.IsEmpty() )
	{
		Bitmap aBmp( rBmpEx.GetBitmap() );
		
		mnInterlaced = 0;	// ( aBmp.GetSizePixel().Width() > 128 ) || ( aBmp.GetSizePixel().Height() > 128 ) ? 1 : 0; #i67236#

		// #i67234# defaulting max chunk size to 256kb when using interlace mode
		mnMaxChunkSize = mnInterlaced == 0 ? std::numeric_limits< sal_uInt32 >::max() : 0x40000;

		if ( pFilterData )
		{
			sal_Int32 i = 0;
			for ( i = 0; i < pFilterData->getLength(); i++ )
			{
				if ( (*pFilterData)[ i ].Name.equalsAscii( "Compression" ) )
					(*pFilterData)[ i ].Value >>= mnCompLevel;
				else if ( (*pFilterData)[ i ].Name.equalsAscii( "Interlaced" ) )
					(*pFilterData)[ i ].Value >>= mnInterlaced;
				else if ( (*pFilterData)[ i ].Name.equalsAscii( "MaxChunkSize" ) )
				{
					sal_Int32 nVal = 0;
					if ( (*pFilterData)[ i ].Value >>= nVal )
						mnMaxChunkSize = (sal_uInt32)nVal;
				}
			}
		}
		mnBitsPerPixel = (sal_uInt8)aBmp.GetBitCount();
		
		if( rBmpEx.IsTransparent() )
		{
			if ( mnBitsPerPixel <= 8 && rBmpEx.IsAlpha() )
			{
				aBmp.Convert( BMP_CONVERSION_24BIT );
				mnBitsPerPixel = 24;
			}
	        
			if ( mnBitsPerPixel <= 8 )					// transparent palette
			{
				aBmp.Convert( BMP_CONVERSION_8BIT_TRANS );
				aBmp.Replace( rBmpEx.GetMask(), BMP_COL_TRANS );
				mnBitsPerPixel = 8;
				mpAccess = aBmp.AcquireReadAccess();
				if ( mpAccess )
				{
					if ( ImplWriteHeader() )
					{
						ImplWritepHYs( rBmpEx );
						ImplWritePalette();
						ImplWriteTransparent();
						ImplWriteIDAT();
					}
					aBmp.ReleaseAccess( mpAccess );
				}
				else
					mbStatus = sal_False;
			}
			else
			{
				mpAccess = aBmp.AcquireReadAccess();	// sal_True RGB with alphachannel
				if( mpAccess )
				{
					if ( ( mbTrueAlpha = rBmpEx.IsAlpha() ) != sal_False )
					{
						AlphaMask aMask( rBmpEx.GetAlpha() );
						mpMaskAccess = aMask.AcquireReadAccess();
						if ( mpMaskAccess )
						{
							if ( ImplWriteHeader() )
							{
								ImplWritepHYs( rBmpEx );
								ImplWriteIDAT();
							}
							aMask.ReleaseAccess( mpMaskAccess );
						}
						else
							mbStatus = sal_False;
					}
					else
					{
						Bitmap aMask( rBmpEx.GetMask() );
						mpMaskAccess = aMask.AcquireReadAccess();
						if( mpMaskAccess )
						{
							if ( ImplWriteHeader() )
							{
								ImplWritepHYs( rBmpEx );
								ImplWriteIDAT();
							}
							aMask.ReleaseAccess( mpMaskAccess );
						}
						else
							mbStatus = sal_False;
					}
					aBmp.ReleaseAccess( mpAccess );
				}
				else
					mbStatus = sal_False;
			}
		}
		else
		{
			mpAccess = aBmp.AcquireReadAccess();		// palette + RGB without alphachannel
			if( mpAccess )
			{
				if ( ImplWriteHeader() )
				{
					ImplWritepHYs( rBmpEx );
					if( mpAccess->HasPalette() )
						ImplWritePalette();

					ImplWriteIDAT();
				}
				aBmp.ReleaseAccess( mpAccess );
			}
			else
				mbStatus = sal_False;
		}
		if ( mbStatus )
		{
			ImplOpenChunk( PNGCHUNK_IEND );		// create an IEND chunk
			ImplCloseChunk();
		}
	}
}

// ------------------------------------------------------------------------

PNGWriterImpl::~PNGWriterImpl()
{
	delete mpZCodec;
}

// ------------------------------------------------------------------------

sal_Bool PNGWriterImpl::Write( SvStream& rOStm )
{
   /* png signature is always an array of 8 bytes */
	sal_uInt16 nOldMode = rOStm.GetNumberFormatInt();
	rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
	rOStm << static_cast<sal_uInt32>(0x89504e47);
	rOStm << static_cast<sal_uInt32>(0x0d0a1a0a);

	std::vector< vcl::PNGWriter::ChunkData >::iterator aBeg( maChunkSeq.begin() );
	std::vector< vcl::PNGWriter::ChunkData >::iterator aEnd( maChunkSeq.end() );
	while( aBeg != aEnd )
	{
		sal_uInt32 nType = aBeg->nType;
	#if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
		nType = SWAPLONG( nType );
	#endif
		sal_uInt32 nCRC = rtl_crc32( 0, &nType, 4 );
		sal_uInt32 nDataSize = aBeg->aData.size();
		if ( nDataSize )
			nCRC = rtl_crc32( nCRC, &aBeg->aData[ 0 ], nDataSize );
		rOStm << nDataSize
			  << aBeg->nType;
		if ( nDataSize )
		    rOStm.Write( &aBeg->aData[ 0 ], nDataSize );
		rOStm << nCRC;
		aBeg++;
	}
	rOStm.SetNumberFormatInt( nOldMode );
	return mbStatus;
}

// ------------------------------------------------------------------------

std::vector< vcl::PNGWriter::ChunkData >& PNGWriterImpl::GetChunks()
{
	return maChunkSeq;
}

// ------------------------------------------------------------------------

sal_Bool PNGWriterImpl::ImplWriteHeader()
{
	ImplOpenChunk(PNGCHUNK_IHDR);
	ImplWriteChunk( sal_uInt32( mnWidth =  mpAccess->Width() ) );
	ImplWriteChunk( sal_uInt32( mnHeight = mpAccess->Height() ) );

	if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus )
	{
		sal_uInt8 nBitDepth = mnBitsPerPixel;
		if ( mnBitsPerPixel <= 8 )
			mnFilterType = 0;
		else
			mnFilterType = 4;

		sal_uInt8 nColorType = 2;					// colortype:
												// bit 0 -> palette is used
		if ( mpAccess->HasPalette() )			// bit 1 -> color is used
			nColorType |= 1;					// bit 2 -> alpha channel is used
		else
			nBitDepth /= 3;

		if ( mpMaskAccess )
			nColorType |= 4;

		ImplWriteChunk( nBitDepth );
		ImplWriteChunk( nColorType );			// colortype
		ImplWriteChunk((sal_uInt8) 0 );				// compression type
		ImplWriteChunk((sal_uInt8) 0 );				// filter type - is not supported in this version
		ImplWriteChunk((sal_uInt8) mnInterlaced );	// interlace type
		ImplCloseChunk();
	}
	else
		mbStatus = sal_False;
	return mbStatus;
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplWritePalette()
{
	const sal_uLong	nCount = mpAccess->GetPaletteEntryCount();
	sal_uInt8*		pTempBuf = new sal_uInt8[ nCount*3 ];
	sal_uInt8*		pTmp = pTempBuf;

	ImplOpenChunk( PNGCHUNK_PLTE );

	for ( sal_uInt16 i = 0; i < nCount; i++ )
	{
		const BitmapColor& rColor = mpAccess->GetPaletteColor( i );
		*pTmp++ = rColor.GetRed();
		*pTmp++ = rColor.GetGreen();
		*pTmp++ = rColor.GetBlue();
	}
	ImplWriteChunk( pTempBuf, nCount*3 );
	ImplCloseChunk();
	delete[] pTempBuf;
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplWriteTransparent ()
{
	const sal_uLong nTransIndex = mpAccess->GetBestPaletteIndex( BMP_COL_TRANS );

	ImplOpenChunk( PNGCHUNK_tRNS );

	for ( sal_uLong n = 0UL; n <= nTransIndex; n++ )
		ImplWriteChunk( ( nTransIndex == n ) ? (sal_uInt8) 0x0 : (sal_uInt8) 0xff );

	ImplCloseChunk();
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplWritepHYs( const BitmapEx& rBmpEx )
{
	if ( rBmpEx.GetPrefMapMode() == MAP_100TH_MM )
	{
		Size aPrefSize( rBmpEx.GetPrefSize() );
		if ( aPrefSize.Width() && aPrefSize.Height() )
		{
			ImplOpenChunk( PNGCHUNK_pHYs );
			sal_uInt8 nMapUnit = 1;
			sal_uInt32 nPrefSizeX = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Width() / mnWidth ) + 0.5 );
			sal_uInt32 nPrefSizeY = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Height() / mnHeight ) + 0.5 );
			ImplWriteChunk( nPrefSizeX );
			ImplWriteChunk( nPrefSizeY );
			ImplWriteChunk( nMapUnit );
			ImplCloseChunk();
		}
	}
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplWriteIDAT ()
{
	mnDeflateInSize = mnBitsPerPixel;

	if( mpMaskAccess )
		mnDeflateInSize += 8;

	mnBBP = ( mnDeflateInSize + 7 ) >> 3;

	mnDeflateInSize = mnBBP * mnWidth + 1;

	mpDeflateInBuf = new sal_uInt8[ mnDeflateInSize ];

	if ( mnFilterType )			// using filter type 4 we need memory for the scanline 3 times
	{
		mpPreviousScan = new sal_uInt8[ mnDeflateInSize ];
		mpCurrentScan = new sal_uInt8[ mnDeflateInSize ];
		ImplClearFirstScanline();
	}
	mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT + mnCompLevel );
	mpZCodec->SetCRC( mnCRC );
	SvMemoryStream aOStm;
	if ( mnInterlaced == 0 )
	{
		for ( sal_uLong nY = 0; nY < mnHeight; nY++ )
			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter( nY ) );
	}
	else
	{
		// interlace mode
		sal_uLong nY;
		for ( nY = 0; nY < mnHeight; nY+=8 )												// pass 1
			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 8 ) );
		ImplClearFirstScanline();

		for ( nY = 0; nY < mnHeight; nY+=8 )												// pass 2
			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 4, 8 ) );
		ImplClearFirstScanline();

		if ( mnHeight >= 5 )																// pass 3
		{
			for ( nY = 4; nY < mnHeight; nY+=8 )
				mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 4 ) );
			ImplClearFirstScanline();
		}

		for ( nY = 0; nY < mnHeight; nY+=4 )												// pass 4
			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 2, 4 ) );
		ImplClearFirstScanline();

		if ( mnHeight >= 3 )																// pass 5
		{
			for ( nY = 2; nY < mnHeight; nY+=4 )
				mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 2 ) );
			ImplClearFirstScanline();
		}

		for ( nY = 0; nY < mnHeight; nY+=2 )												// pass 6
			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 1, 2 ) );
		ImplClearFirstScanline();

		if ( mnHeight >= 2 )																// pass 7
		{
			for ( nY = 1; nY < mnHeight; nY+=2 )
				mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 1 ) );
		}
	}
	mpZCodec->EndCompression();
	mnCRC = mpZCodec->GetCRC();

	if ( mnFilterType )			// using filter type 4 we need memory for the scanline 3 times
	{
		delete[] mpCurrentScan;
		delete[] mpPreviousScan;
	}
	delete[] mpDeflateInBuf;

	sal_uInt32 nIDATSize = aOStm.Tell();
	sal_uInt32 nBytes, nBytesToWrite = nIDATSize;
	while( nBytesToWrite )
	{
		nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize;
		ImplOpenChunk( PNGCHUNK_IDAT );
		ImplWriteChunk( (unsigned char*)aOStm.GetData() + ( nIDATSize - nBytesToWrite ), nBytes );
		ImplCloseChunk();
		nBytesToWrite -= nBytes;
	}
}

// ---------------------------------------------------------------------------------------------------
// ImplGetFilter writes the complete Scanline (nY) - in interlace mode the parameter nXStart and nXAdd
// appends to the currently used pass
// the complete size of scanline will be returned - in interlace mode zero is possible!

sal_uLong PNGWriterImpl::ImplGetFilter ( sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd )
{
	sal_uInt8* pDest;

	if ( mnFilterType )
		pDest = mpCurrentScan;
	else
		pDest = mpDeflateInBuf;

	if ( nXStart < mnWidth )
	{
		*pDest++ = mnFilterType;		// in this version the filter type is either 0 or 4

		if ( mpAccess->HasPalette() )	// alphachannel is not allowed by pictures including palette entries
		{
			switch ( mnBitsPerPixel )
			{
				case( 1 ):
				{
					sal_uLong nX, nXIndex;
					for ( nX = nXStart, nXIndex = 0; nX < mnWidth; nX+=nXAdd, nXIndex++ )
					{
						sal_uLong nShift = ( nXIndex & 7 ) ^ 7;
						if ( nShift == 7)
							*pDest = mpAccess->GetPixelIndex( nY, nX ) << nShift;
						else if  ( nShift == 0 )
							*pDest++ |= mpAccess->GetPixelIndex( nY, nX ) << nShift;
						else
							*pDest |= mpAccess->GetPixelIndex( nY, nX ) << nShift;
					}
					if ( ( nXIndex & 7 ) != 0 ) pDest++;	// byte is not completely used, so the
				}											// bufferpointer is to correct
				break;

				case( 4 ):
				{
					sal_uLong nX, nXIndex;
					for ( nX = nXStart, nXIndex = 0; nX < mnWidth; nX+= nXAdd, nXIndex++ )
					{
						if( nXIndex & 1 )
							*pDest++ |= mpAccess->GetPixelIndex( nY, nX );
						else
							*pDest = mpAccess->GetPixelIndex( nY, nX ) << 4;
					}
					if ( nXIndex & 1 ) pDest++;
				}
				break;

				case( 8 ):
				{
					for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
						*pDest++ = mpAccess->GetPixelIndex( nY, nX );
				}
				break;

				default :
					mbStatus = sal_False;
				break;
			}
		}
		else
		{
			if ( mpMaskAccess )				// mpMaskAccess != NULL -> alphachannel is to create
			{
				if ( mbTrueAlpha )
				{
					for ( sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd )
					{
						const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
						*pDest++ = rColor.GetRed();
						*pDest++ = rColor.GetGreen();
						*pDest++ = rColor.GetBlue();
						*pDest++ = 255 - mpMaskAccess->GetPixelIndex( nY, nX );
					}
				}
				else
				{
					const BitmapColor aTrans( mpMaskAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );

					for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
					{
						const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
						*pDest++ = rColor.GetRed();
						*pDest++ = rColor.GetGreen();
						*pDest++ = rColor.GetBlue();

						if( mpMaskAccess->GetPixel( nY, nX ) == aTrans )
							*pDest++ = 0;
						else
							*pDest++ = 0xff;
					}
				}
			}
			else
			{
				for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
				{
					const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
					*pDest++ = rColor.GetRed();
					*pDest++ = rColor.GetGreen();
					*pDest++ = rColor.GetBlue();
				}
			}
		}
	}
	// filter type4 ( PAETH ) will be used only for 24bit graphics
	if ( mnFilterType )
	{
		mnDeflateInSize = pDest - mpCurrentScan;
		pDest = mpDeflateInBuf;
		*pDest++ = 4;									// filter type

		sal_uLong na, nb, nc;
		long  np, npa, npb, npc;

		sal_uInt8* p1 = mpCurrentScan + 1;					// Current Pixel
		sal_uInt8* p2 = p1 - mnBBP;							// left pixel
		sal_uInt8* p3 = mpPreviousScan;						// upper pixel
		sal_uInt8* p4 = p3 - mnBBP;							// upperleft Pixel;

		while ( pDest < mpDeflateInBuf + mnDeflateInSize )
		{
			nb = *p3++;
			if ( p2 >= mpCurrentScan + 1 )
			{
				na = *p2;
				nc = *p4;
			}
			else
				na = nc = 0;

			np = na + nb;
			np -= nc;
			npa = np - na;
			npb = np - nb;
			npc = np - nc;
			if ( npa < 0 )
				npa =-npa;
			if ( npb < 0 )
				npb =-npb;
			if ( npc < 0 )
				npc =-npc;
			if ( ( npa <= npb ) && ( npa <= npc ) ) *pDest++ = *p1++ - (sal_uInt8)na;
			else if ( npb <= npc ) *pDest++ = *p1++ - (sal_uInt8)nb;
			else *pDest++ = *p1++ - (sal_uInt8)nc;
			p4++;
			p2++;
		}
		for ( long i = 0; i < (long)( mnDeflateInSize - 1 ); i++ )
			mpPreviousScan[ i ] = mpCurrentScan[ i + 1 ];
	}
	else
		mnDeflateInSize = pDest - mpDeflateInBuf;
	return ( mnDeflateInSize );
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplClearFirstScanline()
{
	if ( mnFilterType )
		rtl_zeroMemory( mpPreviousScan, mnDeflateInSize );
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplOpenChunk ( sal_uLong nChunkType )
{
	maChunkSeq.resize( maChunkSeq.size() + 1 );
	maChunkSeq.back().nType = nChunkType;
}

// ------------------------------------------------------------------------

void PNGWriterImpl::ImplWriteChunk ( sal_uInt8 nSource )
{
	maChunkSeq.back().aData.push_back( nSource );
}

void PNGWriterImpl::ImplWriteChunk ( sal_uInt32 nSource )
{
	vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
	rChunkData.aData.push_back( (sal_uInt8)( nSource >> 24 ) );
	rChunkData.aData.push_back( (sal_uInt8)( nSource >> 16 ) );
	rChunkData.aData.push_back( (sal_uInt8)( nSource >> 8 ) );
	rChunkData.aData.push_back( (sal_uInt8)( nSource ) );
}

void PNGWriterImpl::ImplWriteChunk ( unsigned char* pSource, sal_uInt32 nDatSize )
{
	if ( nDatSize )
	{
		vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
		sal_uInt32 nSize = rChunkData.aData.size();
		rChunkData.aData.resize( nSize + nDatSize );
		rtl_copyMemory( &rChunkData.aData[ nSize ], pSource, nDatSize );
	}
}

// ------------------------------------------------------------------------
// nothing to do
void PNGWriterImpl::ImplCloseChunk ( void )
{
}

// -------------
// - PNGWriter -
// -------------

PNGWriter::PNGWriter( const BitmapEx& rBmpEx,
	const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) :
	mpImpl( new ::vcl::PNGWriterImpl( rBmpEx, pFilterData ) )
{
}

// ------------------------------------------------------------------------

PNGWriter::~PNGWriter()
{
	delete mpImpl;
}

// ------------------------------------------------------------------------

sal_Bool PNGWriter::Write( SvStream& rIStm )
{
	return mpImpl->Write( rIStm );
}

// ------------------------------------------------------------------------

std::vector< vcl::PNGWriter::ChunkData >& PNGWriter::GetChunks()
{
	return mpImpl->GetChunks();
}

} // namespace vcl

