xref: /AOO41X/main/vcl/source/gdi/pngwrite.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include <vcl/pngwrite.hxx>
32 
33 #include <cmath>
34 #include <limits>
35 #include <rtl/crc.h>
36 #include <rtl/memory.h>
37 #include <rtl/alloc.h>
38 #include <tools/zcodec.hxx>
39 #include <tools/stream.hxx>
40 #include <vcl/bmpacc.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/alpha.hxx>
43 #include <osl/endian.h>
44 
45 // -----------
46 // - Defines -
47 // -----------
48 
49 #define PNG_DEF_COMPRESSION 6
50 
51 #define PNGCHUNK_IHDR 0x49484452
52 #define PNGCHUNK_PLTE 0x504c5445
53 #define PNGCHUNK_IDAT 0x49444154
54 #define PNGCHUNK_IEND 0x49454e44
55 #define PNGCHUNK_bKGD 0x624b4744
56 #define PNGCHUNK_cHRM 0x6348524d
57 #define PNGCHUNK_gAMA 0x67414d41
58 #define PNGCHUNK_hIST 0x68495354
59 #define PNGCHUNK_pHYs 0x70485973
60 #define PNGCHUNK_sBIT 0x73425420
61 #define PNGCHUNK_tIME 0x74494d45
62 #define PNGCHUNK_tEXt 0x74455874
63 #define PNGCHUNK_tRNS 0x74524e53
64 #define PNGCHUNK_zTXt 0x7a545874
65 
66 namespace vcl
67 {
68 // -----------------
69 // - PNGWriterImplImpl -
70 // -----------------
71 
72 class PNGWriterImpl
73 {
74 public:
75 
76 				PNGWriterImpl( const BitmapEx& BmpEx,
77 					const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData = NULL );
78 				~PNGWriterImpl();
79 
80 	sal_Bool	Write( SvStream& rOStm );
81 
82 	std::vector< vcl::PNGWriter::ChunkData >&	GetChunks();
83 
84 private:
85 
86 	std::vector< vcl::PNGWriter::ChunkData >	maChunkSeq;
87 
88 	sal_Int32			mnCompLevel;
89 	sal_Int32			mnInterlaced;
90 	sal_uInt32			mnMaxChunkSize;
91 	sal_Bool				mbStatus;
92 
93 	BitmapReadAccess*	mpAccess;
94 	BitmapReadAccess*	mpMaskAccess;
95 	ZCodec*				mpZCodec;
96 
97 	sal_uInt8*				mpDeflateInBuf;			// as big as the size of a scanline + alphachannel + 1
98 	sal_uInt8*				mpPreviousScan;			// as big as mpDeflateInBuf
99 	sal_uInt8*				mpCurrentScan;
100 	sal_uLong				mnDeflateInSize;
101 
102 	sal_uLong				mnWidth, mnHeight;
103 	sal_uInt8				mnBitsPerPixel;
104 	sal_uInt8				mnFilterType;			// 0 oder 4;
105 	sal_uLong				mnBBP;					// bytes per pixel ( needed for filtering )
106 	sal_Bool				mbTrueAlpha;
107 	sal_uLong				mnCRC;
108 	long				mnChunkDatSize;
109 	sal_uLong				mnLastPercent;
110 
111 	void				ImplWritepHYs( const BitmapEx& rBitmapEx );
112 	void				ImplWriteIDAT();
113 	sal_uLong				ImplGetFilter( sal_uLong nY, sal_uLong nXStart=0, sal_uLong nXAdd=1 );
114 	void				ImplClearFirstScanline();
115 	void				ImplWriteTransparent();
116 	sal_Bool				ImplWriteHeader();
117 	void				ImplWritePalette();
118 	void				ImplOpenChunk( sal_uLong nChunkType );
119 	void				ImplWriteChunk( sal_uInt8 nNumb );
120 	void				ImplWriteChunk( sal_uInt32 nNumb );
121 	void				ImplWriteChunk( unsigned char* pSource, sal_uInt32 nDatSize );
122 	void				ImplCloseChunk( void );
123 };
124 
125 // ------------------------------------------------------------------------
126 
127 PNGWriterImpl::PNGWriterImpl( const BitmapEx& rBmpEx,
128 	const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) :
129 		mnCompLevel		( PNG_DEF_COMPRESSION ),
130 		mbStatus		( sal_True ),
131 		mpAccess		( NULL ),
132 		mpMaskAccess	( NULL ),
133 		mpZCodec		( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ),
134         mnCRC(0UL),
135 		mnLastPercent	( 0UL )
136 {
137 	if ( !rBmpEx.IsEmpty() )
138 	{
139 		Bitmap aBmp( rBmpEx.GetBitmap() );
140 
141 		mnInterlaced = 0;	// ( aBmp.GetSizePixel().Width() > 128 ) || ( aBmp.GetSizePixel().Height() > 128 ) ? 1 : 0; #i67236#
142 
143 		// #i67234# defaulting max chunk size to 256kb when using interlace mode
144 		mnMaxChunkSize = mnInterlaced == 0 ? std::numeric_limits< sal_uInt32 >::max() : 0x40000;
145 
146 		if ( pFilterData )
147 		{
148 			sal_Int32 i = 0;
149 			for ( i = 0; i < pFilterData->getLength(); i++ )
150 			{
151 				if ( (*pFilterData)[ i ].Name.equalsAscii( "Compression" ) )
152 					(*pFilterData)[ i ].Value >>= mnCompLevel;
153 				else if ( (*pFilterData)[ i ].Name.equalsAscii( "Interlaced" ) )
154 					(*pFilterData)[ i ].Value >>= mnInterlaced;
155 				else if ( (*pFilterData)[ i ].Name.equalsAscii( "MaxChunkSize" ) )
156 				{
157 					sal_Int32 nVal = 0;
158 					if ( (*pFilterData)[ i ].Value >>= nVal )
159 						mnMaxChunkSize = (sal_uInt32)nVal;
160 				}
161 			}
162 		}
163 		mnBitsPerPixel = (sal_uInt8)aBmp.GetBitCount();
164 
165 		if( rBmpEx.IsTransparent() )
166 		{
167 			if ( mnBitsPerPixel <= 8 && rBmpEx.IsAlpha() )
168 			{
169 				aBmp.Convert( BMP_CONVERSION_24BIT );
170 				mnBitsPerPixel = 24;
171 			}
172 
173 			if ( mnBitsPerPixel <= 8 )					// transparent palette
174 			{
175 				aBmp.Convert( BMP_CONVERSION_8BIT_TRANS );
176 				aBmp.Replace( rBmpEx.GetMask(), BMP_COL_TRANS );
177 				mnBitsPerPixel = 8;
178 				mpAccess = aBmp.AcquireReadAccess();
179 				if ( mpAccess )
180 				{
181 					if ( ImplWriteHeader() )
182 					{
183 						ImplWritepHYs( rBmpEx );
184 						ImplWritePalette();
185 						ImplWriteTransparent();
186 						ImplWriteIDAT();
187 					}
188 					aBmp.ReleaseAccess( mpAccess );
189 				}
190 				else
191 					mbStatus = sal_False;
192 			}
193 			else
194 			{
195 				mpAccess = aBmp.AcquireReadAccess();	// sal_True RGB with alphachannel
196 				if( mpAccess )
197 				{
198 					if ( ( mbTrueAlpha = rBmpEx.IsAlpha() ) != sal_False )
199 					{
200 						AlphaMask aMask( rBmpEx.GetAlpha() );
201 						mpMaskAccess = aMask.AcquireReadAccess();
202 						if ( mpMaskAccess )
203 						{
204 							if ( ImplWriteHeader() )
205 							{
206 								ImplWritepHYs( rBmpEx );
207 								ImplWriteIDAT();
208 							}
209 							aMask.ReleaseAccess( mpMaskAccess );
210 						}
211 						else
212 							mbStatus = sal_False;
213 					}
214 					else
215 					{
216 						Bitmap aMask( rBmpEx.GetMask() );
217 						mpMaskAccess = aMask.AcquireReadAccess();
218 						if( mpMaskAccess )
219 						{
220 							if ( ImplWriteHeader() )
221 							{
222 								ImplWritepHYs( rBmpEx );
223 								ImplWriteIDAT();
224 							}
225 							aMask.ReleaseAccess( mpMaskAccess );
226 						}
227 						else
228 							mbStatus = sal_False;
229 					}
230 					aBmp.ReleaseAccess( mpAccess );
231 				}
232 				else
233 					mbStatus = sal_False;
234 			}
235 		}
236 		else
237 		{
238 			mpAccess = aBmp.AcquireReadAccess();		// palette + RGB without alphachannel
239 			if( mpAccess )
240 			{
241 				if ( ImplWriteHeader() )
242 				{
243 					ImplWritepHYs( rBmpEx );
244 					if( mpAccess->HasPalette() )
245 						ImplWritePalette();
246 
247 					ImplWriteIDAT();
248 				}
249 				aBmp.ReleaseAccess( mpAccess );
250 			}
251 			else
252 				mbStatus = sal_False;
253 		}
254 		if ( mbStatus )
255 		{
256 			ImplOpenChunk( PNGCHUNK_IEND );		// create an IEND chunk
257 			ImplCloseChunk();
258 		}
259 	}
260 }
261 
262 // ------------------------------------------------------------------------
263 
264 PNGWriterImpl::~PNGWriterImpl()
265 {
266 	delete mpZCodec;
267 }
268 
269 // ------------------------------------------------------------------------
270 
271 sal_Bool PNGWriterImpl::Write( SvStream& rOStm )
272 {
273    /* png signature is always an array of 8 bytes */
274 	sal_uInt16 nOldMode = rOStm.GetNumberFormatInt();
275 	rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
276 	rOStm << static_cast<sal_uInt32>(0x89504e47);
277 	rOStm << static_cast<sal_uInt32>(0x0d0a1a0a);
278 
279 	std::vector< vcl::PNGWriter::ChunkData >::iterator aBeg( maChunkSeq.begin() );
280 	std::vector< vcl::PNGWriter::ChunkData >::iterator aEnd( maChunkSeq.end() );
281 	while( aBeg != aEnd )
282 	{
283 		sal_uInt32 nType = aBeg->nType;
284 	#if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
285 		nType = SWAPLONG( nType );
286 	#endif
287 		sal_uInt32 nCRC = rtl_crc32( 0, &nType, 4 );
288 		sal_uInt32 nDataSize = aBeg->aData.size();
289 		if ( nDataSize )
290 			nCRC = rtl_crc32( nCRC, &aBeg->aData[ 0 ], nDataSize );
291 		rOStm << nDataSize
292 			  << aBeg->nType;
293 		if ( nDataSize )
294 		    rOStm.Write( &aBeg->aData[ 0 ], nDataSize );
295 		rOStm << nCRC;
296 		aBeg++;
297 	}
298 	rOStm.SetNumberFormatInt( nOldMode );
299 	return mbStatus;
300 }
301 
302 // ------------------------------------------------------------------------
303 
304 std::vector< vcl::PNGWriter::ChunkData >& PNGWriterImpl::GetChunks()
305 {
306 	return maChunkSeq;
307 }
308 
309 // ------------------------------------------------------------------------
310 
311 sal_Bool PNGWriterImpl::ImplWriteHeader()
312 {
313 	ImplOpenChunk(PNGCHUNK_IHDR);
314 	ImplWriteChunk( sal_uInt32( mnWidth =  mpAccess->Width() ) );
315 	ImplWriteChunk( sal_uInt32( mnHeight = mpAccess->Height() ) );
316 
317 	if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus )
318 	{
319 		sal_uInt8 nBitDepth = mnBitsPerPixel;
320 		if ( mnBitsPerPixel <= 8 )
321 			mnFilterType = 0;
322 		else
323 			mnFilterType = 4;
324 
325 		sal_uInt8 nColorType = 2;					// colortype:
326 												// bit 0 -> palette is used
327 		if ( mpAccess->HasPalette() )			// bit 1 -> color is used
328 			nColorType |= 1;					// bit 2 -> alpha channel is used
329 		else
330 			nBitDepth /= 3;
331 
332 		if ( mpMaskAccess )
333 			nColorType |= 4;
334 
335 		ImplWriteChunk( nBitDepth );
336 		ImplWriteChunk( nColorType );			// colortype
337 		ImplWriteChunk((sal_uInt8) 0 );				// compression type
338 		ImplWriteChunk((sal_uInt8) 0 );				// filter type - is not supported in this version
339 		ImplWriteChunk((sal_uInt8) mnInterlaced );	// interlace type
340 		ImplCloseChunk();
341 	}
342 	else
343 		mbStatus = sal_False;
344 	return mbStatus;
345 }
346 
347 // ------------------------------------------------------------------------
348 
349 void PNGWriterImpl::ImplWritePalette()
350 {
351 	const sal_uLong	nCount = mpAccess->GetPaletteEntryCount();
352 	sal_uInt8*		pTempBuf = new sal_uInt8[ nCount*3 ];
353 	sal_uInt8*		pTmp = pTempBuf;
354 
355 	ImplOpenChunk( PNGCHUNK_PLTE );
356 
357 	for ( sal_uInt16 i = 0; i < nCount; i++ )
358 	{
359 		const BitmapColor& rColor = mpAccess->GetPaletteColor( i );
360 		*pTmp++ = rColor.GetRed();
361 		*pTmp++ = rColor.GetGreen();
362 		*pTmp++ = rColor.GetBlue();
363 	}
364 	ImplWriteChunk( pTempBuf, nCount*3 );
365 	ImplCloseChunk();
366 	delete[] pTempBuf;
367 }
368 
369 // ------------------------------------------------------------------------
370 
371 void PNGWriterImpl::ImplWriteTransparent ()
372 {
373 	const sal_uLong nTransIndex = mpAccess->GetBestMatchingColor( BMP_COL_TRANS );
374 
375 	ImplOpenChunk( PNGCHUNK_tRNS );
376 
377 	for ( sal_uLong n = 0UL; n <= nTransIndex; n++ )
378 		ImplWriteChunk( ( nTransIndex == n ) ? (sal_uInt8) 0x0 : (sal_uInt8) 0xff );
379 
380 	ImplCloseChunk();
381 }
382 
383 // ------------------------------------------------------------------------
384 
385 void PNGWriterImpl::ImplWritepHYs( const BitmapEx& rBmpEx )
386 {
387 	if ( rBmpEx.GetPrefMapMode() == MAP_100TH_MM )
388 	{
389 		Size aPrefSize( rBmpEx.GetPrefSize() );
390 		if ( aPrefSize.Width() && aPrefSize.Height() )
391 		{
392 			ImplOpenChunk( PNGCHUNK_pHYs );
393 			sal_uInt8 nMapUnit = 1;
394 			sal_uInt32 nPrefSizeX = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Width() / mnWidth ) + 0.5 );
395 			sal_uInt32 nPrefSizeY = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Height() / mnHeight ) + 0.5 );
396 			ImplWriteChunk( nPrefSizeX );
397 			ImplWriteChunk( nPrefSizeY );
398 			ImplWriteChunk( nMapUnit );
399 			ImplCloseChunk();
400 		}
401 	}
402 }
403 
404 // ------------------------------------------------------------------------
405 
406 void PNGWriterImpl::ImplWriteIDAT ()
407 {
408 	mnDeflateInSize = mnBitsPerPixel;
409 
410 	if( mpMaskAccess )
411 		mnDeflateInSize += 8;
412 
413 	mnBBP = ( mnDeflateInSize + 7 ) >> 3;
414 
415 	mnDeflateInSize = mnBBP * mnWidth + 1;
416 
417 	mpDeflateInBuf = new sal_uInt8[ mnDeflateInSize ];
418 
419 	if ( mnFilterType )			// using filter type 4 we need memory for the scanline 3 times
420 	{
421 		mpPreviousScan = new sal_uInt8[ mnDeflateInSize ];
422 		mpCurrentScan = new sal_uInt8[ mnDeflateInSize ];
423 		ImplClearFirstScanline();
424 	}
425 	mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT + mnCompLevel );
426 	mpZCodec->SetCRC( mnCRC );
427 	SvMemoryStream aOStm;
428 	if ( mnInterlaced == 0 )
429 	{
430 		for ( sal_uLong nY = 0; nY < mnHeight; nY++ )
431 			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter( nY ) );
432 	}
433 	else
434 	{
435 		// interlace mode
436 		sal_uLong nY;
437 		for ( nY = 0; nY < mnHeight; nY+=8 )												// pass 1
438 			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 8 ) );
439 		ImplClearFirstScanline();
440 
441 		for ( nY = 0; nY < mnHeight; nY+=8 )												// pass 2
442 			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 4, 8 ) );
443 		ImplClearFirstScanline();
444 
445 		if ( mnHeight >= 5 )																// pass 3
446 		{
447 			for ( nY = 4; nY < mnHeight; nY+=8 )
448 				mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 4 ) );
449 			ImplClearFirstScanline();
450 		}
451 
452 		for ( nY = 0; nY < mnHeight; nY+=4 )												// pass 4
453 			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 2, 4 ) );
454 		ImplClearFirstScanline();
455 
456 		if ( mnHeight >= 3 )																// pass 5
457 		{
458 			for ( nY = 2; nY < mnHeight; nY+=4 )
459 				mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 2 ) );
460 			ImplClearFirstScanline();
461 		}
462 
463 		for ( nY = 0; nY < mnHeight; nY+=2 )												// pass 6
464 			mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 1, 2 ) );
465 		ImplClearFirstScanline();
466 
467 		if ( mnHeight >= 2 )																// pass 7
468 		{
469 			for ( nY = 1; nY < mnHeight; nY+=2 )
470 				mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 1 ) );
471 		}
472 	}
473 	mpZCodec->EndCompression();
474 	mnCRC = mpZCodec->GetCRC();
475 
476 	if ( mnFilterType )			// using filter type 4 we need memory for the scanline 3 times
477 	{
478 		delete[] mpCurrentScan;
479 		delete[] mpPreviousScan;
480 	}
481 	delete[] mpDeflateInBuf;
482 
483 	sal_uInt32 nIDATSize = aOStm.Tell();
484 	sal_uInt32 nBytes, nBytesToWrite = nIDATSize;
485 	while( nBytesToWrite )
486 	{
487 		nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize;
488 		ImplOpenChunk( PNGCHUNK_IDAT );
489 		ImplWriteChunk( (unsigned char*)aOStm.GetData() + ( nIDATSize - nBytesToWrite ), nBytes );
490 		ImplCloseChunk();
491 		nBytesToWrite -= nBytes;
492 	}
493 }
494 
495 // ---------------------------------------------------------------------------------------------------
496 // ImplGetFilter writes the complete Scanline (nY) - in interlace mode the parameter nXStart and nXAdd
497 // appends to the currently used pass
498 // the complete size of scanline will be returned - in interlace mode zero is possible!
499 
500 sal_uLong PNGWriterImpl::ImplGetFilter ( sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd )
501 {
502 	sal_uInt8* pDest;
503 
504 	if ( mnFilterType )
505 		pDest = mpCurrentScan;
506 	else
507 		pDest = mpDeflateInBuf;
508 
509 	if ( nXStart < mnWidth )
510 	{
511 		*pDest++ = mnFilterType;		// in this version the filter type is either 0 or 4
512 
513 		if ( mpAccess->HasPalette() )	// alphachannel is not allowed by pictures including palette entries
514 		{
515 			switch ( mnBitsPerPixel )
516 			{
517 				case( 1 ):
518 				{
519 					sal_uLong nX, nXIndex;
520 					for ( nX = nXStart, nXIndex = 0; nX < mnWidth; nX+=nXAdd, nXIndex++ )
521 					{
522 						sal_uLong nShift = ( nXIndex & 7 ) ^ 7;
523 						if ( nShift == 7)
524 							*pDest = (sal_uInt8)(mpAccess->GetPixel( nY, nX ) << nShift);
525 						else if  ( nShift == 0 )
526 							*pDest++ |= (sal_uInt8) mpAccess->GetPixel( nY, nX ) << nShift;
527 						else
528 							*pDest |= (sal_uInt8) mpAccess->GetPixel( nY, nX ) << nShift;
529 					}
530 					if ( ( nXIndex & 7 ) != 0 ) pDest++;	// byte is not completely used, so the
531 				}											// bufferpointer is to correct
532 				break;
533 
534 				case( 4 ):
535 				{
536 					sal_uLong nX, nXIndex;
537 					for ( nX = nXStart, nXIndex = 0; nX < mnWidth; nX+= nXAdd, nXIndex++ )
538 					{
539 						if( nXIndex & 1 )
540 							*pDest++ |= (sal_uInt8) mpAccess->GetPixel( nY, nX );
541 						else
542 							*pDest = (sal_uInt8) mpAccess->GetPixel( nY, nX ) << 4;
543 					}
544 					if ( nXIndex & 1 ) pDest++;
545 				}
546 				break;
547 
548 				case( 8 ):
549 				{
550 					for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
551 						*pDest++ = mpAccess->GetPixel( nY, nX );
552 				}
553 				break;
554 
555 				default :
556 					mbStatus = sal_False;
557 				break;
558 			}
559 		}
560 		else
561 		{
562 			if ( mpMaskAccess )				// mpMaskAccess != NULL -> alphachannel is to create
563 			{
564 				if ( mbTrueAlpha )
565 				{
566 					for ( sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd )
567 					{
568 						const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
569 						*pDest++ = rColor.GetRed();
570 						*pDest++ = rColor.GetGreen();
571 						*pDest++ = rColor.GetBlue();
572 						*pDest++ = 255 - mpMaskAccess->GetPixel( nY, nX );
573 					}
574 				}
575 				else
576 				{
577 					const BitmapColor aTrans( mpMaskAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );
578 
579 					for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
580 					{
581 						const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
582 						*pDest++ = rColor.GetRed();
583 						*pDest++ = rColor.GetGreen();
584 						*pDest++ = rColor.GetBlue();
585 
586 						if( mpMaskAccess->GetPixel( nY, nX ) == aTrans )
587 							*pDest++ = 0;
588 						else
589 							*pDest++ = 0xff;
590 					}
591 				}
592 			}
593 			else
594 			{
595 				for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
596 				{
597 					const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
598 					*pDest++ = rColor.GetRed();
599 					*pDest++ = rColor.GetGreen();
600 					*pDest++ = rColor.GetBlue();
601 				}
602 			}
603 		}
604 	}
605 	// filter type4 ( PAETH ) will be used only for 24bit graphics
606 	if ( mnFilterType )
607 	{
608 		mnDeflateInSize = pDest - mpCurrentScan;
609 		pDest = mpDeflateInBuf;
610 		*pDest++ = 4;									// filter type
611 
612 		sal_uLong na, nb, nc;
613 		long  np, npa, npb, npc;
614 
615 		sal_uInt8* p1 = mpCurrentScan + 1;					// Current Pixel
616 		sal_uInt8* p2 = p1 - mnBBP;							// left pixel
617 		sal_uInt8* p3 = mpPreviousScan;						// upper pixel
618 		sal_uInt8* p4 = p3 - mnBBP;							// upperleft Pixel;
619 
620 		while ( pDest < mpDeflateInBuf + mnDeflateInSize )
621 		{
622 			nb = *p3++;
623 			if ( p2 >= mpCurrentScan + 1 )
624 			{
625 				na = *p2;
626 				nc = *p4;
627 			}
628 			else
629 				na = nc = 0;
630 
631 			np = na + nb;
632 			np -= nc;
633 			npa = np - na;
634 			npb = np - nb;
635 			npc = np - nc;
636 			if ( npa < 0 )
637 				npa =-npa;
638 			if ( npb < 0 )
639 				npb =-npb;
640 			if ( npc < 0 )
641 				npc =-npc;
642 			if ( ( npa <= npb ) && ( npa <= npc ) ) *pDest++ = *p1++ - (sal_uInt8)na;
643 			else if ( npb <= npc ) *pDest++ = *p1++ - (sal_uInt8)nb;
644 			else *pDest++ = *p1++ - (sal_uInt8)nc;
645 			p4++;
646 			p2++;
647 		}
648 		for ( long i = 0; i < (long)( mnDeflateInSize - 1 ); i++ )
649 			mpPreviousScan[ i ] = mpCurrentScan[ i + 1 ];
650 	}
651 	else
652 		mnDeflateInSize = pDest - mpDeflateInBuf;
653 	return ( mnDeflateInSize );
654 }
655 
656 // ------------------------------------------------------------------------
657 
658 void PNGWriterImpl::ImplClearFirstScanline()
659 {
660 	if ( mnFilterType )
661 		rtl_zeroMemory( mpPreviousScan, mnDeflateInSize );
662 }
663 
664 // ------------------------------------------------------------------------
665 
666 void PNGWriterImpl::ImplOpenChunk ( sal_uLong nChunkType )
667 {
668 	maChunkSeq.resize( maChunkSeq.size() + 1 );
669 	maChunkSeq.back().nType = nChunkType;
670 }
671 
672 // ------------------------------------------------------------------------
673 
674 void PNGWriterImpl::ImplWriteChunk ( sal_uInt8 nSource )
675 {
676 	maChunkSeq.back().aData.push_back( nSource );
677 }
678 
679 void PNGWriterImpl::ImplWriteChunk ( sal_uInt32 nSource )
680 {
681 	vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
682 	rChunkData.aData.push_back( (sal_uInt8)( nSource >> 24 ) );
683 	rChunkData.aData.push_back( (sal_uInt8)( nSource >> 16 ) );
684 	rChunkData.aData.push_back( (sal_uInt8)( nSource >> 8 ) );
685 	rChunkData.aData.push_back( (sal_uInt8)( nSource ) );
686 }
687 
688 void PNGWriterImpl::ImplWriteChunk ( unsigned char* pSource, sal_uInt32 nDatSize )
689 {
690 	if ( nDatSize )
691 	{
692 		vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
693 		sal_uInt32 nSize = rChunkData.aData.size();
694 		rChunkData.aData.resize( nSize + nDatSize );
695 		rtl_copyMemory( &rChunkData.aData[ nSize ], pSource, nDatSize );
696 	}
697 }
698 
699 // ------------------------------------------------------------------------
700 // nothing to do
701 void PNGWriterImpl::ImplCloseChunk ( void )
702 {
703 }
704 
705 // -------------
706 // - PNGWriter -
707 // -------------
708 
709 PNGWriter::PNGWriter( const BitmapEx& rBmpEx,
710 	const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) :
711 	mpImpl( new ::vcl::PNGWriterImpl( rBmpEx, pFilterData ) )
712 {
713 }
714 
715 // ------------------------------------------------------------------------
716 
717 PNGWriter::~PNGWriter()
718 {
719 	delete mpImpl;
720 }
721 
722 // ------------------------------------------------------------------------
723 
724 sal_Bool PNGWriter::Write( SvStream& rIStm )
725 {
726 	return mpImpl->Write( rIStm );
727 }
728 
729 // ------------------------------------------------------------------------
730 
731 std::vector< vcl::PNGWriter::ChunkData >& PNGWriter::GetChunks()
732 {
733 	return mpImpl->GetChunks();
734 }
735 
736 } // namespace vcl
737 
738