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