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 :
21 : #include <vcl/pngwrite.hxx>
22 :
23 : #include <cmath>
24 : #include <limits>
25 : #include <rtl/crc.h>
26 : #include <rtl/alloc.h>
27 : #include <tools/zcodec.hxx>
28 : #include <tools/stream.hxx>
29 : #include <vcl/bmpacc.hxx>
30 : #include <vcl/svapp.hxx>
31 : #include <vcl/alpha.hxx>
32 : #include <osl/endian.h>
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 : 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 : ~PNGWriterImpl();
53 :
54 : sal_Bool Write( SvStream& rOStm );
55 :
56 : std::vector< vcl::PNGWriter::ChunkData >& GetChunks();
57 :
58 : private:
59 :
60 : std::vector< vcl::PNGWriter::ChunkData > maChunkSeq;
61 :
62 : sal_Int32 mnCompLevel;
63 : sal_Int32 mnInterlaced;
64 : sal_uInt32 mnMaxChunkSize;
65 : sal_Bool mbStatus;
66 :
67 : BitmapReadAccess* mpAccess;
68 : BitmapReadAccess* mpMaskAccess;
69 : ZCodec* mpZCodec;
70 :
71 : sal_uInt8* mpDeflateInBuf; // as big as the size of a scanline + alphachannel + 1
72 : sal_uInt8* mpPreviousScan; // as big as mpDeflateInBuf
73 : sal_uInt8* mpCurrentScan;
74 : sal_uLong mnDeflateInSize;
75 :
76 : sal_uLong mnWidth, mnHeight;
77 : sal_uInt8 mnBitsPerPixel;
78 : sal_uInt8 mnFilterType; // 0 oder 4;
79 : sal_uLong mnBBP; // bytes per pixel ( needed for filtering )
80 : sal_Bool mbTrueAlpha;
81 : sal_uLong mnCRC;
82 :
83 : void ImplWritepHYs( const BitmapEx& rBitmapEx );
84 : void ImplWriteIDAT();
85 : sal_uLong ImplGetFilter( sal_uLong nY, sal_uLong nXStart=0, sal_uLong nXAdd=1 );
86 : void ImplClearFirstScanline();
87 : void ImplWriteTransparent();
88 : sal_Bool ImplWriteHeader();
89 : void ImplWritePalette();
90 : void ImplOpenChunk( sal_uLong nChunkType );
91 : void ImplWriteChunk( sal_uInt8 nNumb );
92 : void ImplWriteChunk( sal_uInt32 nNumb );
93 : void ImplWriteChunk( unsigned char* pSource, sal_uInt32 nDatSize );
94 : void ImplCloseChunk( void ) const;
95 : };
96 :
97 55 : PNGWriterImpl::PNGWriterImpl( const BitmapEx& rBmpEx,
98 : const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) :
99 : mnCompLevel ( PNG_DEF_COMPRESSION ),
100 : mbStatus ( sal_True ),
101 : mpAccess ( NULL ),
102 : mpMaskAccess ( NULL ),
103 55 : mpZCodec ( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ),
104 110 : mnCRC(0UL)
105 : {
106 55 : if ( !rBmpEx.IsEmpty() )
107 : {
108 55 : Bitmap aBmp( rBmpEx.GetBitmap() );
109 :
110 55 : mnInterlaced = 0; // ( aBmp.GetSizePixel().Width() > 128 ) || ( aBmp.GetSizePixel().Height() > 128 ) ? 1 : 0; #i67236#
111 :
112 : // #i67234# defaulting max chunk size to 256kb when using interlace mode
113 55 : mnMaxChunkSize = mnInterlaced == 0 ? std::numeric_limits< sal_uInt32 >::max() : 0x40000;
114 :
115 55 : if ( pFilterData )
116 : {
117 1 : sal_Int32 i = 0;
118 4 : for ( i = 0; i < pFilterData->getLength(); i++ )
119 : {
120 3 : if ( (*pFilterData)[ i ].Name == "Compression" )
121 1 : (*pFilterData)[ i ].Value >>= mnCompLevel;
122 2 : else if ( (*pFilterData)[ i ].Name == "Interlaced" )
123 1 : (*pFilterData)[ i ].Value >>= mnInterlaced;
124 1 : else if ( (*pFilterData)[ i ].Name == "MaxChunkSize" )
125 : {
126 0 : sal_Int32 nVal = 0;
127 0 : if ( (*pFilterData)[ i ].Value >>= nVal )
128 0 : mnMaxChunkSize = (sal_uInt32)nVal;
129 : }
130 : }
131 : }
132 55 : mnBitsPerPixel = (sal_uInt8)aBmp.GetBitCount();
133 :
134 55 : if( rBmpEx.IsTransparent() )
135 : {
136 2 : if ( mnBitsPerPixel <= 8 && rBmpEx.IsAlpha() )
137 : {
138 0 : aBmp.Convert( BMP_CONVERSION_24BIT );
139 0 : mnBitsPerPixel = 24;
140 : }
141 :
142 2 : if ( mnBitsPerPixel <= 8 ) // transparent palette
143 : {
144 0 : aBmp.Convert( BMP_CONVERSION_8BIT_TRANS );
145 0 : aBmp.Replace( rBmpEx.GetMask(), BMP_COL_TRANS );
146 0 : mnBitsPerPixel = 8;
147 0 : mpAccess = aBmp.AcquireReadAccess();
148 0 : if ( mpAccess )
149 : {
150 0 : if ( ImplWriteHeader() )
151 : {
152 0 : ImplWritepHYs( rBmpEx );
153 0 : ImplWritePalette();
154 0 : ImplWriteTransparent();
155 0 : ImplWriteIDAT();
156 : }
157 0 : aBmp.ReleaseAccess( mpAccess ), mpAccess = 0;
158 : }
159 : else
160 0 : mbStatus = sal_False;
161 : }
162 : else
163 : {
164 2 : mpAccess = aBmp.AcquireReadAccess(); // sal_True RGB with alphachannel
165 2 : if( mpAccess )
166 : {
167 2 : if ( ( mbTrueAlpha = rBmpEx.IsAlpha() ) != sal_False )
168 : {
169 0 : AlphaMask aMask( rBmpEx.GetAlpha() );
170 0 : mpMaskAccess = aMask.AcquireReadAccess();
171 0 : if ( mpMaskAccess )
172 : {
173 0 : if ( ImplWriteHeader() )
174 : {
175 0 : ImplWritepHYs( rBmpEx );
176 0 : ImplWriteIDAT();
177 : }
178 0 : aMask.ReleaseAccess( mpMaskAccess ), mpMaskAccess = 0;
179 : }
180 : else
181 0 : mbStatus = sal_False;
182 : }
183 : else
184 : {
185 2 : Bitmap aMask( rBmpEx.GetMask() );
186 2 : mpMaskAccess = aMask.AcquireReadAccess();
187 2 : if( mpMaskAccess )
188 : {
189 2 : if ( ImplWriteHeader() )
190 : {
191 2 : ImplWritepHYs( rBmpEx );
192 2 : ImplWriteIDAT();
193 : }
194 2 : aMask.ReleaseAccess( mpMaskAccess ), mpMaskAccess = 0;
195 : }
196 : else
197 0 : mbStatus = sal_False;
198 : }
199 2 : aBmp.ReleaseAccess( mpAccess ), mpAccess = 0;
200 : }
201 : else
202 0 : mbStatus = sal_False;
203 : }
204 : }
205 : else
206 : {
207 53 : mpAccess = aBmp.AcquireReadAccess(); // palette + RGB without alphachannel
208 53 : if( mpAccess )
209 : {
210 53 : if ( ImplWriteHeader() )
211 : {
212 53 : ImplWritepHYs( rBmpEx );
213 53 : if( mpAccess->HasPalette() )
214 0 : ImplWritePalette();
215 :
216 53 : ImplWriteIDAT();
217 : }
218 53 : aBmp.ReleaseAccess( mpAccess ), mpAccess = 0;
219 : }
220 : else
221 0 : mbStatus = sal_False;
222 : }
223 55 : if ( mbStatus )
224 : {
225 55 : ImplOpenChunk( PNGCHUNK_IEND ); // create an IEND chunk
226 55 : ImplCloseChunk();
227 55 : }
228 : }
229 55 : }
230 :
231 110 : PNGWriterImpl::~PNGWriterImpl()
232 : {
233 55 : delete mpZCodec;
234 55 : }
235 :
236 55 : sal_Bool PNGWriterImpl::Write( SvStream& rOStm )
237 : {
238 : /* png signature is always an array of 8 bytes */
239 55 : sal_uInt16 nOldMode = rOStm.GetNumberFormatInt();
240 55 : rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
241 55 : rOStm << static_cast<sal_uInt32>(0x89504e47);
242 55 : rOStm << static_cast<sal_uInt32>(0x0d0a1a0a);
243 :
244 55 : std::vector< vcl::PNGWriter::ChunkData >::iterator aBeg( maChunkSeq.begin() );
245 55 : std::vector< vcl::PNGWriter::ChunkData >::iterator aEnd( maChunkSeq.end() );
246 277 : while( aBeg != aEnd )
247 : {
248 167 : sal_uInt32 nType = aBeg->nType;
249 : #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
250 167 : nType = OSL_SWAPDWORD( nType );
251 : #endif
252 167 : sal_uInt32 nCRC = rtl_crc32( 0, &nType, 4 );
253 167 : sal_uInt32 nDataSize = aBeg->aData.size();
254 167 : if ( nDataSize )
255 112 : nCRC = rtl_crc32( nCRC, &aBeg->aData[ 0 ], nDataSize );
256 167 : rOStm << nDataSize
257 334 : << aBeg->nType;
258 167 : if ( nDataSize )
259 112 : rOStm.Write( &aBeg->aData[ 0 ], nDataSize );
260 167 : rOStm << nCRC;
261 167 : ++aBeg;
262 : }
263 55 : rOStm.SetNumberFormatInt( nOldMode );
264 55 : return mbStatus;
265 : }
266 :
267 0 : std::vector< vcl::PNGWriter::ChunkData >& PNGWriterImpl::GetChunks()
268 : {
269 0 : return maChunkSeq;
270 : }
271 :
272 55 : sal_Bool PNGWriterImpl::ImplWriteHeader()
273 : {
274 55 : ImplOpenChunk(PNGCHUNK_IHDR);
275 55 : ImplWriteChunk( sal_uInt32( mnWidth = mpAccess->Width() ) );
276 55 : ImplWriteChunk( sal_uInt32( mnHeight = mpAccess->Height() ) );
277 :
278 55 : if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus )
279 : {
280 55 : sal_uInt8 nBitDepth = mnBitsPerPixel;
281 55 : if ( mnBitsPerPixel <= 8 )
282 0 : mnFilterType = 0;
283 : else
284 55 : mnFilterType = 4;
285 :
286 55 : sal_uInt8 nColorType = 2; // colortype:
287 : // bit 0 -> palette is used
288 55 : if ( mpAccess->HasPalette() ) // bit 1 -> color is used
289 0 : nColorType |= 1; // bit 2 -> alpha channel is used
290 : else
291 55 : nBitDepth /= 3;
292 :
293 55 : if ( mpMaskAccess )
294 2 : nColorType |= 4;
295 :
296 55 : ImplWriteChunk( nBitDepth );
297 55 : ImplWriteChunk( nColorType ); // colortype
298 55 : ImplWriteChunk((sal_uInt8) 0 ); // compression type
299 55 : ImplWriteChunk((sal_uInt8) 0 ); // filter type - is not supported in this version
300 55 : ImplWriteChunk((sal_uInt8) mnInterlaced ); // interlace type
301 55 : ImplCloseChunk();
302 : }
303 : else
304 0 : mbStatus = sal_False;
305 55 : return mbStatus;
306 : }
307 :
308 0 : void PNGWriterImpl::ImplWritePalette()
309 : {
310 0 : const sal_uLong nCount = mpAccess->GetPaletteEntryCount();
311 0 : sal_uInt8* pTempBuf = new sal_uInt8[ nCount*3 ];
312 0 : sal_uInt8* pTmp = pTempBuf;
313 :
314 0 : ImplOpenChunk( PNGCHUNK_PLTE );
315 :
316 0 : for ( sal_uInt16 i = 0; i < nCount; i++ )
317 : {
318 0 : const BitmapColor& rColor = mpAccess->GetPaletteColor( i );
319 0 : *pTmp++ = rColor.GetRed();
320 0 : *pTmp++ = rColor.GetGreen();
321 0 : *pTmp++ = rColor.GetBlue();
322 : }
323 0 : ImplWriteChunk( pTempBuf, nCount*3 );
324 0 : ImplCloseChunk();
325 0 : delete[] pTempBuf;
326 0 : }
327 :
328 0 : void PNGWriterImpl::ImplWriteTransparent ()
329 : {
330 0 : const sal_uLong nTransIndex = mpAccess->GetBestPaletteIndex( BMP_COL_TRANS );
331 :
332 0 : ImplOpenChunk( PNGCHUNK_tRNS );
333 :
334 0 : for ( sal_uLong n = 0UL; n <= nTransIndex; n++ )
335 0 : ImplWriteChunk( ( nTransIndex == n ) ? (sal_uInt8) 0x0 : (sal_uInt8) 0xff );
336 :
337 0 : ImplCloseChunk();
338 0 : }
339 :
340 55 : void PNGWriterImpl::ImplWritepHYs( const BitmapEx& rBmpEx )
341 : {
342 55 : if ( rBmpEx.GetPrefMapMode() == MAP_100TH_MM )
343 : {
344 2 : Size aPrefSize( rBmpEx.GetPrefSize() );
345 2 : if ( aPrefSize.Width() && aPrefSize.Height() )
346 : {
347 2 : ImplOpenChunk( PNGCHUNK_pHYs );
348 2 : sal_uInt8 nMapUnit = 1;
349 2 : sal_uInt32 nPrefSizeX = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Width() / mnWidth ) + 0.5 );
350 2 : sal_uInt32 nPrefSizeY = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Height() / mnHeight ) + 0.5 );
351 2 : ImplWriteChunk( nPrefSizeX );
352 2 : ImplWriteChunk( nPrefSizeY );
353 2 : ImplWriteChunk( nMapUnit );
354 2 : ImplCloseChunk();
355 : }
356 : }
357 55 : }
358 :
359 55 : void PNGWriterImpl::ImplWriteIDAT ()
360 : {
361 55 : mnDeflateInSize = mnBitsPerPixel;
362 :
363 55 : if( mpMaskAccess )
364 2 : mnDeflateInSize += 8;
365 :
366 55 : mnBBP = ( mnDeflateInSize + 7 ) >> 3;
367 :
368 55 : mnDeflateInSize = mnBBP * mnWidth + 1;
369 :
370 55 : mpDeflateInBuf = new sal_uInt8[ mnDeflateInSize ];
371 :
372 55 : if ( mnFilterType ) // using filter type 4 we need memory for the scanline 3 times
373 : {
374 55 : mpPreviousScan = new sal_uInt8[ mnDeflateInSize ];
375 55 : mpCurrentScan = new sal_uInt8[ mnDeflateInSize ];
376 55 : ImplClearFirstScanline();
377 : }
378 55 : mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT + mnCompLevel );
379 55 : mpZCodec->SetCRC( mnCRC );
380 55 : SvMemoryStream aOStm;
381 55 : if ( mnInterlaced == 0 )
382 : {
383 6552 : for ( sal_uLong nY = 0; nY < mnHeight; nY++ )
384 6497 : 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 55 : mpZCodec->EndCompression();
427 55 : mnCRC = mpZCodec->GetCRC();
428 :
429 55 : if ( mnFilterType ) // using filter type 4 we need memory for the scanline 3 times
430 : {
431 55 : delete[] mpCurrentScan;
432 55 : delete[] mpPreviousScan;
433 : }
434 55 : delete[] mpDeflateInBuf;
435 :
436 55 : sal_uInt32 nIDATSize = aOStm.Tell();
437 55 : sal_uInt32 nBytes, nBytesToWrite = nIDATSize;
438 165 : while( nBytesToWrite )
439 : {
440 55 : nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize;
441 55 : ImplOpenChunk( PNGCHUNK_IDAT );
442 55 : ImplWriteChunk( (unsigned char*)aOStm.GetData() + ( nIDATSize - nBytesToWrite ), nBytes );
443 55 : ImplCloseChunk();
444 55 : nBytesToWrite -= nBytes;
445 55 : }
446 55 : }
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 6497 : sal_uLong PNGWriterImpl::ImplGetFilter ( sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd )
453 : {
454 : sal_uInt8* pDest;
455 :
456 6497 : if ( mnFilterType )
457 6497 : pDest = mpCurrentScan;
458 : else
459 0 : pDest = mpDeflateInBuf;
460 :
461 6497 : if ( nXStart < mnWidth )
462 : {
463 6497 : *pDest++ = mnFilterType; // in this version the filter type is either 0 or 4
464 :
465 6497 : if ( mpAccess->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
466 : {
467 0 : 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 0 : for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
503 0 : *pDest++ = mpAccess->GetPixelIndex( nY, nX );
504 : }
505 0 : break;
506 :
507 : default :
508 0 : mbStatus = sal_False;
509 0 : break;
510 : }
511 : }
512 : else
513 : {
514 6497 : if ( mpMaskAccess ) // mpMaskAccess != NULL -> alphachannel is to create
515 : {
516 11 : if ( mbTrueAlpha )
517 : {
518 0 : for ( sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd )
519 : {
520 0 : const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
521 0 : *pDest++ = rColor.GetRed();
522 0 : *pDest++ = rColor.GetGreen();
523 0 : *pDest++ = rColor.GetBlue();
524 0 : *pDest++ = 255 - mpMaskAccess->GetPixelIndex( nY, nX );
525 0 : }
526 : }
527 : else
528 : {
529 11 : const BitmapColor aTrans( mpMaskAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );
530 :
531 96 : for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
532 : {
533 85 : const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
534 85 : *pDest++ = rColor.GetRed();
535 85 : *pDest++ = rColor.GetGreen();
536 85 : *pDest++ = rColor.GetBlue();
537 :
538 85 : if( mpMaskAccess->GetPixel( nY, nX ) == aTrans )
539 33 : *pDest++ = 0;
540 : else
541 52 : *pDest++ = 0xff;
542 96 : }
543 : }
544 : }
545 : else
546 : {
547 1164422 : for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd )
548 : {
549 1157936 : const BitmapColor& rColor = mpAccess->GetPixel( nY, nX );
550 1157936 : *pDest++ = rColor.GetRed();
551 1157936 : *pDest++ = rColor.GetGreen();
552 1157936 : *pDest++ = rColor.GetBlue();
553 1157936 : }
554 : }
555 : }
556 : }
557 : // filter type4 ( PAETH ) will be used only for 24bit graphics
558 6497 : if ( mnFilterType )
559 : {
560 6497 : mnDeflateInSize = pDest - mpCurrentScan;
561 6497 : pDest = mpDeflateInBuf;
562 6497 : *pDest++ = 4; // filter type
563 :
564 : sal_uLong na, nb, nc;
565 : long np, npa, npb, npc;
566 :
567 6497 : sal_uInt8* p1 = mpCurrentScan + 1; // Current Pixel
568 6497 : sal_uInt8* p2 = p1 - mnBBP; // left pixel
569 6497 : sal_uInt8* p3 = mpPreviousScan; // upper pixel
570 6497 : sal_uInt8* p4 = p3 - mnBBP; // upperleft Pixel;
571 :
572 3487142 : while ( pDest < mpDeflateInBuf + mnDeflateInSize )
573 : {
574 3474148 : nb = *p3++;
575 3474148 : if ( p2 >= mpCurrentScan + 1 )
576 : {
577 3454646 : na = *p2;
578 3454646 : nc = *p4;
579 : }
580 : else
581 19502 : na = nc = 0;
582 :
583 3474148 : np = na + nb;
584 3474148 : np -= nc;
585 3474148 : npa = np - na;
586 3474148 : npb = np - nb;
587 3474148 : npc = np - nc;
588 3474148 : if ( npa < 0 )
589 61888 : npa =-npa;
590 3474148 : if ( npb < 0 )
591 77370 : npb =-npb;
592 3474148 : if ( npc < 0 )
593 97776 : npc =-npc;
594 3474148 : if ( ( npa <= npb ) && ( npa <= npc ) ) *pDest++ = *p1++ - (sal_uInt8)na;
595 94489 : else if ( npb <= npc ) *pDest++ = *p1++ - (sal_uInt8)nb;
596 11396 : else *pDest++ = *p1++ - (sal_uInt8)nc;
597 3474148 : p4++;
598 3474148 : p2++;
599 : }
600 3480645 : for ( long i = 0; i < (long)( mnDeflateInSize - 1 ); i++ )
601 3474148 : mpPreviousScan[ i ] = mpCurrentScan[ i + 1 ];
602 : }
603 : else
604 0 : mnDeflateInSize = pDest - mpDeflateInBuf;
605 6497 : return ( mnDeflateInSize );
606 : }
607 :
608 55 : void PNGWriterImpl::ImplClearFirstScanline()
609 : {
610 55 : if ( mnFilterType )
611 55 : memset( mpPreviousScan, 0, mnDeflateInSize );
612 55 : }
613 :
614 167 : void PNGWriterImpl::ImplOpenChunk ( sal_uLong nChunkType )
615 : {
616 167 : maChunkSeq.resize( maChunkSeq.size() + 1 );
617 167 : maChunkSeq.back().nType = nChunkType;
618 167 : }
619 :
620 277 : void PNGWriterImpl::ImplWriteChunk ( sal_uInt8 nSource )
621 : {
622 277 : maChunkSeq.back().aData.push_back( nSource );
623 277 : }
624 :
625 114 : void PNGWriterImpl::ImplWriteChunk ( sal_uInt32 nSource )
626 : {
627 114 : vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
628 114 : rChunkData.aData.push_back( (sal_uInt8)( nSource >> 24 ) );
629 114 : rChunkData.aData.push_back( (sal_uInt8)( nSource >> 16 ) );
630 114 : rChunkData.aData.push_back( (sal_uInt8)( nSource >> 8 ) );
631 114 : rChunkData.aData.push_back( (sal_uInt8)( nSource ) );
632 114 : }
633 :
634 55 : void PNGWriterImpl::ImplWriteChunk ( unsigned char* pSource, sal_uInt32 nDatSize )
635 : {
636 55 : if ( nDatSize )
637 : {
638 55 : vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
639 55 : sal_uInt32 nSize = rChunkData.aData.size();
640 55 : rChunkData.aData.resize( nSize + nDatSize );
641 55 : memcpy( &rChunkData.aData[ nSize ], pSource, nDatSize );
642 : }
643 55 : }
644 :
645 : // nothing to do
646 167 : void PNGWriterImpl::ImplCloseChunk ( void ) const
647 : {
648 167 : }
649 :
650 55 : PNGWriter::PNGWriter( const BitmapEx& rBmpEx,
651 : const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) :
652 55 : mpImpl( new ::vcl::PNGWriterImpl( rBmpEx, pFilterData ) )
653 : {
654 55 : }
655 :
656 55 : PNGWriter::~PNGWriter()
657 : {
658 55 : delete mpImpl;
659 55 : }
660 :
661 55 : sal_Bool PNGWriter::Write( SvStream& rIStm )
662 : {
663 55 : return mpImpl->Write( rIStm );
664 : }
665 :
666 0 : std::vector< vcl::PNGWriter::ChunkData >& PNGWriter::GetChunks()
667 : {
668 0 : return mpImpl->GetChunks();
669 : }
670 :
671 465 : } // namespace vcl
672 :
673 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|