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