xref: /trunk/main/vcl/source/gdi/pngwrite.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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