xref: /trunk/main/vcl/source/gdi/pngread.cxx (revision cbfee35e)
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/pngread.hxx>
28 
29 #include <cmath>
30 #include <rtl/crc.h>
31 #include <rtl/memory.h>
32 #include <rtl/alloc.h>
33 #include <tools/zcodec.hxx>
34 #include <tools/stream.hxx>
35 #include <vcl/bmpacc.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/alpha.hxx>
38 #include <osl/endian.h>
39 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
40 
41 // -----------
42 // - Defines -
43 // -----------
44 
45 #define PNGCHUNK_IHDR 		0x49484452
46 #define PNGCHUNK_PLTE 		0x504c5445
47 #define PNGCHUNK_IDAT 		0x49444154
48 #define PNGCHUNK_IEND 		0x49454e44
49 #define PNGCHUNK_bKGD 		0x624b4744
50 #define PNGCHUNK_cHRM		0x6348524d
51 #define PNGCHUNK_gAMA 		0x67414d41
52 #define PNGCHUNK_hIST 		0x68495354
53 #define PNGCHUNK_pHYs 		0x70485973
54 #define PNGCHUNK_sBIT 		0x73425420
55 #define PNGCHUNK_tIME		0x74494d45
56 #define PNGCHUNK_tEXt		0x74455874
57 #define PNGCHUNK_tRNS 		0x74524e53
58 #define PNGCHUNK_zTXt 		0x7a545874
59 #define PMGCHUNG_msOG		0x6d734f47		// Microsoft Office Animated GIF
60 
61 #define VIEWING_GAMMA		2.35
62 #define DISPLAY_GAMMA		1.0
63 
64 namespace vcl
65 {
66 // -----------
67 // - statics -
68 // -----------
69 
70 // ------------------------------------------------------------------------------
71 
72 static const sal_uInt8 mpDefaultColorTable[ 256 ] =
73 {	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
74 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
75 	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
76 	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
77 	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
78 	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
79 	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
80 	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
81 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
82 	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
83 	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
84 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
85 	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
86 	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
87 	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
88 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
89 };
90 
91 // -------------
92 // - PNGReaderImpl -
93 // -------------
94 
95 class PNGReaderImpl
96 {
97 private:
98     SvStream&           mrPNGStream;
99     sal_uInt16          mnOrigStreamMode;
100 
101     std::vector< vcl::PNGReader::ChunkData >    maChunkSeq;
102     std::vector< vcl::PNGReader::ChunkData >::iterator maChunkIter;
103     std::vector< sal_uInt8 >::iterator          maDataIter;
104 
105 	Bitmap*				mpBmp;
106 	BitmapWriteAccess*	mpAcc;
107 	Bitmap*				mpMaskBmp;
108 	AlphaMask*			mpAlphaMask;
109 	BitmapWriteAccess*	mpMaskAcc;
110 	ZCodec* 			mpZCodec;
111 	sal_uInt8*				mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1
112 	sal_uInt8*				mpScanPrior;    // pointer to the latest scanline
113 	sal_uInt8*				mpTransTab;		// for transparency in images with palette colortype
114 	sal_uInt8*				mpScanCurrent;  // pointer into the current scanline
115 	sal_uInt8*				mpColorTable;	//
116     sal_Size            mnStreamSize;   // estimate of PNG file size
117     sal_uInt32          mnChunkType;    // Type of current PNG chunk
118     sal_Int32           mnChunkLen;     // Length of current PNG chunk
119     Size                maOrigSize;     // pixel size of the full image
120     Size                maTargetSize;   // pixel size of the result image
121     Size                maPhysSize;     // preferred size in MAP_100TH_MM units
122     sal_uInt32          mnBPP;          // number of bytes per pixel
123     sal_uInt32          mnScansize;     // max size of scanline
124     sal_uInt32          mnYpos;         // latest y position in full image
125     int                 mnPass;         // if interlaced the latest pass ( 1..7 ) else 7
126     sal_uInt32          mnXStart;       // the starting X for the current pass
127     sal_uInt32          mnXAdd;         // the increment for input images X coords for the current pass
128     sal_uInt32          mnYAdd;         // the increment for input images Y coords for the current pass
129     int                 mnPreviewShift; // shift to convert orig image coords into preview image coords
130     int                 mnPreviewMask;  // == ((1 << mnPreviewShift) - 1)
131 	sal_uInt16				mnIStmOldMode;
132 	sal_uInt16				mnTargetDepth;		// pixel depth of target bitmap
133 	sal_uInt8				mnTransRed;
134 	sal_uInt8				mnTransGreen;
135 	sal_uInt8				mnTransBlue;
136 	sal_uInt8				mnPngDepth;		// pixel depth of PNG data
137 	sal_uInt8				mnColorType;
138 	sal_uInt8				mnCompressionType;
139 	sal_uInt8				mnFilterType;
140 	sal_uInt8				mnInterlaceType;
141 	BitmapColor			mcTranspColor;  // transparency mask's transparency "color"
142 	BitmapColor			mcOpaqueColor;  // transparency mask's opaque "color"
143 	sal_Bool				mbTransparent;	// graphic includes an tRNS Chunk or an alpha Channel
144 	sal_Bool				mbAlphaChannel;	// is true for ColorType 4 and 6
145 	sal_Bool				mbRGBTriple;
146 	sal_Bool				mbPalette;		// sal_False if we need a Palette
147 	sal_Bool				mbGrayScale;
148 	sal_Bool				mbzCodecInUse;
149 	sal_Bool				mbStatus;
150 	sal_Bool				mbIDAT;			// sal_True if finished with enough IDAT chunks
151 	sal_Bool				mbGamma;		// sal_True if Gamma Correction available
152 	sal_Bool				mbpHYs;			// sal_True if pysical size of pixel available
153 	sal_Bool			mbIgnoreGammaChunk;
154 
155 #ifdef DBG_UTIL
156     // do some checks in debug mode
157     sal_uInt32          mnAllocSizeScanline;
158     sal_uInt32          mnAllocSizeScanlineAlpha;
159 #endif
160     // the temporary Scanline (and alpha) for direct scanline copy to Bitmap
161     sal_uInt8*          mpScanline;
162     sal_uInt8*          mpScanlineAlpha;
163 
164     bool                ReadNextChunk();
165     void                ReadRemainingChunks();
166     void                SkipRemainingChunks();
167 
168     void                ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & );
169     void                ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex );
170     void                ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, sal_Bool bTrans );
171     void                ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha );
172 	void				ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha );
173 	void				ImplReadIDAT();
174     bool                ImplPreparePass();
175     void                ImplApplyFilter();
176     void                ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd );
177 	sal_Bool				ImplReadTransparent();
178 	void				ImplGetGamma();
179 	void				ImplGetBackground();
180 	sal_uInt8				ImplScaleColor();
181 	sal_Bool				ImplReadHeader( const Size& rPreviewSizeHint );
182 	sal_Bool				ImplReadPalette();
183 	void				ImplGetGrayPalette( sal_uInt16 );
184 	sal_uInt32			ImplReadsal_uInt32();
185 
186 public:
187 
188                         PNGReaderImpl( SvStream& );
189                         ~PNGReaderImpl();
190 
191     BitmapEx            GetBitmapEx( const Size& rPreviewSizeHint );
192     const std::vector< PNGReader::ChunkData >& GetAllChunks();
SetIgnoreGammaChunk(sal_Bool bIgnore)193 	void				SetIgnoreGammaChunk( sal_Bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; };
194 };
195 
196 // ------------------------------------------------------------------------------
197 
PNGReaderImpl(SvStream & rPNGStream)198 PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream )
199 :   mrPNGStream( rPNGStream ),
200 	mpBmp			( NULL ),
201 	mpAcc			( NULL ),
202 	mpMaskBmp		( NULL ),
203 	mpAlphaMask		( NULL ),
204 	mpMaskAcc		( NULL ),
205 	mpZCodec		( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ),
206 	mpInflateInBuf	( NULL ),
207 	mpScanPrior 	( NULL ),
208 	mpTransTab		( NULL ),
209 	mpColorTable	( (sal_uInt8*) mpDefaultColorTable ),
210 	mnColorType( 0xFF ),
211 	mbPalette( false ),
212 	mbzCodecInUse	( sal_False ),
213     mbStatus( sal_True),
214     mbIDAT( sal_False ),
215 	mbGamma				( sal_False ),
216 	mbpHYs				( sal_False ),
217 	mbIgnoreGammaChunk	( sal_False ),
218 #ifdef DBG_UTIL
219     mnAllocSizeScanline(0),
220     mnAllocSizeScanlineAlpha(0),
221 #endif
222     mpScanline(0),
223     mpScanlineAlpha(0)
224 {
225     // prepare the PNG data stream
226     mnOrigStreamMode = mrPNGStream.GetNumberFormatInt();
227     mrPNGStream.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
228 
229     // prepare the chunk reader
230     maChunkSeq.reserve( 16 );
231     maChunkIter = maChunkSeq.begin();
232 
233     // estimate PNG file size (to allow sanity checks)
234     const sal_Size nStreamPos = mrPNGStream.Tell();
235     mrPNGStream.Seek( STREAM_SEEK_TO_END );
236     mnStreamSize = mrPNGStream.Tell();
237     mrPNGStream.Seek( nStreamPos );
238 
239     // check the PNG header magic
240     sal_uInt32 nDummy = 0;
241     mrPNGStream >> nDummy;
242     mbStatus = (nDummy == 0x89504e47);
243     mrPNGStream >> nDummy;
244     mbStatus &= (nDummy == 0x0d0a1a0a);
245 
246     mnPreviewShift = 0;
247     mnPreviewMask = (1 << mnPreviewShift) - 1;
248 }
249 
250 // ------------------------------------------------------------------------
251 
~PNGReaderImpl()252 PNGReaderImpl::~PNGReaderImpl()
253 {
254     mrPNGStream.SetNumberFormatInt( mnOrigStreamMode );
255 
256 	if ( mbzCodecInUse )
257 		mpZCodec->EndCompression();
258 
259 	if( mpColorTable != mpDefaultColorTable )
260 		delete[] mpColorTable;
261 
262     delete mpBmp;
263     delete mpAlphaMask;
264     delete mpMaskBmp;
265     delete[] mpTransTab;
266     delete[] mpInflateInBuf;
267     delete[] mpScanPrior;
268     delete mpZCodec;
269 
270     delete[] mpScanline;
271     delete[] mpScanlineAlpha;
272 }
273 
274 // ------------------------------------------------------------------------
275 
ReadNextChunk()276 bool PNGReaderImpl::ReadNextChunk()
277 {
278     if( maChunkIter == maChunkSeq.end() )
279     {
280         // get the next chunk from the stream
281 
282         // unless we are at the end of the PNG stream
283         if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) )
284             return false;
285         if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
286             return false;
287 
288         PNGReader::ChunkData aDummyChunk;
289         maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk );
290         PNGReader::ChunkData& rChunkData = *maChunkIter;
291 
292         // read the chunk header
293         mrPNGStream >> mnChunkLen >> mnChunkType;
294         rChunkData.nType = mnChunkType;
295 
296         // #128377#/#149343# sanity check for chunk length
297         if( mnChunkLen < 0 )
298             return false;
299         const sal_Size nStreamPos = mrPNGStream.Tell();
300         if( nStreamPos + mnChunkLen + 4 >= mnStreamSize )
301             return false;
302 
303         // calculate chunktype CRC (swap it back to original byte order)
304         sal_uInt32 nChunkType = mnChunkType;;
305         #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
306         nChunkType = SWAPLONG( nChunkType );
307         #endif
308         sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 );
309 
310         // read the chunk data and check the CRC
311         if( mnChunkLen && !mrPNGStream.IsEof() )
312         {
313             rChunkData.aData.resize( mnChunkLen );
314 
315             sal_Int32 nBytesRead = 0;
316             do {
317                 sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ];
318                 nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead );
319             } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) );
320 
321             nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen );
322             maDataIter = rChunkData.aData.begin();
323         }
324         sal_uInt32 nCheck;
325         mrPNGStream >> nCheck;
326         if( nCRC32 != nCheck )
327             return false;
328     }
329     else
330     {
331         // the next chunk was already read
332         mnChunkType = (*maChunkIter).nType;
333         mnChunkLen = (*maChunkIter).aData.size();
334         maDataIter = (*maChunkIter).aData.begin();
335     }
336 
337     ++maChunkIter;
338     if( mnChunkType == PNGCHUNK_IEND )
339         return false;
340     return true;
341 }
342 
343 // ------------------------------------------------------------------------
344 
345 // read the remaining chunks from mrPNGStream
ReadRemainingChunks()346 void PNGReaderImpl::ReadRemainingChunks()
347 {
348     while( ReadNextChunk() ) ;
349 }
350 
351 // ------------------------------------------------------------------------
352 
353 // move position of mrPNGStream to the end of the file
SkipRemainingChunks()354 void PNGReaderImpl::SkipRemainingChunks()
355 {
356     // nothing to skip if the last chunk was read
357     if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
358         return;
359 
360     // read from the stream until the IEND chunk is found
361     const sal_Size nStreamPos = mrPNGStream.Tell();
362     while( !mrPNGStream.IsEof() && (mrPNGStream.GetError() == ERRCODE_NONE) )
363     {
364         mrPNGStream >> mnChunkLen >> mnChunkType;
365         if( mnChunkLen < 0 )
366             break;
367         if( nStreamPos + mnChunkLen >= mnStreamSize )
368             break;
369         mrPNGStream.SeekRel( mnChunkLen + 4 );  // skip data + CRC
370         if( mnChunkType == PNGCHUNK_IEND )
371             break;
372     }
373 }
374 
375 // ------------------------------------------------------------------------
376 
GetAllChunks()377 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks()
378 {
379     ReadRemainingChunks();
380     return maChunkSeq;
381 }
382 
383 // ------------------------------------------------------------------------
384 
GetBitmapEx(const Size & rPreviewSizeHint)385 BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint )
386 {
387     // reset to the first chunk
388     maChunkIter = maChunkSeq.begin();
389 
390     // read the first chunk which must be the IHDR chunk
391     ReadNextChunk();
392     mbStatus = (mnChunkType == PNGCHUNK_IHDR) && ImplReadHeader( rPreviewSizeHint );
393 
394     // parse the chunks
395     while( mbStatus && !mbIDAT && ReadNextChunk() )
396     {
397         switch( mnChunkType )
398 		{
399 			case PNGCHUNK_IHDR :
400 			{
401 				mbStatus = false; // only one IHDR possible
402 			}
403 			break;
404 
405 			case PNGCHUNK_gAMA :								// the gamma chunk must precede
406 			{													// the 'IDAT' and also the 'PLTE'(if available )
407 				if ( !mbIgnoreGammaChunk && ( mbIDAT == sal_False ) )
408 					ImplGetGamma();
409 			}
410 			break;
411 
412 			case PNGCHUNK_PLTE :
413 			{
414 				if ( !mbPalette )
415 					mbStatus = ImplReadPalette();
416 			}
417 			break;
418 
419 			case PNGCHUNK_tRNS :
420 			{
421 				if ( !mbIDAT )									// the tRNS chunk must precede the IDAT
422 					mbStatus = ImplReadTransparent();
423 			}
424 			break;
425 
426 			case PNGCHUNK_bKGD :								// the background chunk must appear
427 			{
428 				if ( ( mbIDAT == sal_False ) && mbPalette ) 		// before the 'IDAT' and after the
429 					ImplGetBackground();						// PLTE(if available ) chunk.
430 			}
431 			break;
432 
433 			case PNGCHUNK_IDAT :
434 			{
435 				if ( !mpInflateInBuf )	// taking care that the header has properly been read
436 					mbStatus = sal_False;
437 				else if ( !mbIDAT )		// the gfx is finished, but there may be left a zlibCRC of about 4Bytes
438 				{
439 					try
440 					{
441 						ImplReadIDAT();
442 					}
443 					catch (::com::sun::star::lang::IndexOutOfBoundsException&)
444 					{
445 						mbStatus = sal_False;
446 					}
447 				}
448 			}
449 			break;
450 
451 			case PNGCHUNK_pHYs :
452 			{
453 				if ( !mbIDAT && mnChunkLen == 9 )
454 				{
455                     sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32();
456                     sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32();
457 
458                     sal_uInt8 nUnitSpecifier = *maDataIter++;
459                     if( (nUnitSpecifier == 1) && nXPixelPerMeter && nXPixelPerMeter )
460                     {
461                         mbpHYs = sal_True;
462 
463                         // convert into MAP_100TH_MM
464                         maPhysSize.Width()  = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter );
465                         maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter );
466                     }
467 				}
468 			}
469 			break;
470 
471             case PNGCHUNK_IEND:
472                 mbStatus = mbIDAT;  // there is a problem if the image is not complete yet
473             break;
474 		}
475 	}
476 
477     // release write access of the bitmaps
478 	if ( mpAcc )
479 		mpBmp->ReleaseAccess( mpAcc ), mpAcc = NULL;
480 
481 	if ( mpMaskAcc )
482 	{
483 		if ( mpAlphaMask )
484 			mpAlphaMask->ReleaseAccess( mpMaskAcc );
485 		else if ( mpMaskBmp )
486 			mpMaskBmp->ReleaseAccess( mpMaskAcc );
487 
488 		mpMaskAcc = NULL;
489 	}
490 
491     // return the resulting BitmapEx
492     BitmapEx aRet;
493 
494     if( !mbStatus || !mbIDAT )
495         aRet.Clear();
496     else
497 	{
498 		if ( mpAlphaMask )
499 			aRet = BitmapEx( *mpBmp, *mpAlphaMask );
500 		else if ( mpMaskBmp )
501 			aRet = BitmapEx( *mpBmp, *mpMaskBmp );
502 		else
503 			aRet = *mpBmp;
504 
505 		if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() )
506 		{
507 			aRet.SetPrefMapMode( MAP_100TH_MM );
508 			aRet.SetPrefSize( maPhysSize );
509 		}
510 
511 #if 0
512         // TODO: make sure nobody depends on the stream being after the IEND chunks
513         // => let them do ReadChunks before
514         ReadRemainingChunks();
515 #endif
516 	}
517 
518 	return aRet;
519 }
520 
521 // ------------------------------------------------------------------------
522 
ImplReadHeader(const Size & rPreviewSizeHint)523 sal_Bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint )
524 {
525     if( mnChunkLen < 13 )
526         return sal_False;
527 
528     maOrigSize.Width()  = ImplReadsal_uInt32();
529     maOrigSize.Height() = ImplReadsal_uInt32();
530 
531     if ( !maOrigSize.Width() || !maOrigSize.Height() )
532         return sal_False;
533 
534     mnPngDepth = *(maDataIter++);
535     mnColorType = *(maDataIter++);
536 
537     mnCompressionType = *(maDataIter++);
538     if( mnCompressionType != 0 )    // unknown compression type
539         return sal_False;
540 
541     mnFilterType = *(maDataIter++);
542     if( mnFilterType != 0 )         // unknown filter type
543         return sal_False;
544 
545     mnInterlaceType = *(maDataIter++);
546     switch ( mnInterlaceType ) // filter type valid ?
547 	{
548 		case 0 :  // progressive image
549 			mnPass = 7;
550 			break;
551 		case 1 :  // Adam7-interlaced image
552 			mnPass = 0;
553 			break;
554 		default:
555 			return sal_False;
556 	}
557 
558 	mbPalette = sal_True;
559 	mbIDAT = mbAlphaChannel = mbTransparent = sal_False;
560 	mbGrayScale = mbRGBTriple = sal_False;
561 	mnTargetDepth = mnPngDepth;
562 	sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3;
563 
564 	// valid color types are 0,2,3,4 & 6
565 	switch ( mnColorType )
566 	{
567 		case 0 :	// each pixel is a grayscale
568 		{
569 			switch ( mnPngDepth )
570 			{
571 				case 2 : // 2bit target not available -> use four bits
572 					mnTargetDepth = 4;	// we have to expand the bitmap
573 					mbGrayScale = sal_True;
574 					break;
575 				case 16 :
576 					mnTargetDepth = 8;	// we have to reduce the bitmap
577 					// fall through
578 				case 1 :
579 				case 4 :
580 				case 8 :
581 					mbGrayScale = sal_True;
582 					break;
583 				default :
584 					return sal_False;
585 			}
586 		}
587 		break;
588 
589 		case 2 :	// each pixel is an RGB triple
590 		{
591 			mbRGBTriple = sal_True;
592 			nScansize64 *= 3;
593 			switch ( mnPngDepth )
594 			{
595 				case 16 :			// we have to reduce the bitmap
596 				case 8 :
597 					mnTargetDepth = 24;
598 					break;
599 				default :
600 					return sal_False;
601 			}
602 		}
603 		break;
604 
605 		case 3 :	// each pixel is a palette index
606 		{
607 			switch ( mnPngDepth )
608 			{
609 				case 2 :
610 					mnTargetDepth = 4;	// we have to expand the bitmap
611 					// fall through
612 				case 1 :
613 				case 4 :
614 				case 8 :
615 					mbPalette = sal_False;
616 					break;
617 				default :
618 					return sal_False;
619 			}
620 		}
621 		break;
622 
623 		case 4 :	// each pixel is a grayscale sample followed by an alpha sample
624 		{
625 			nScansize64 *= 2;
626 			mbAlphaChannel = sal_True;
627 			switch ( mnPngDepth )
628 			{
629 				case 16 :
630 					mnTargetDepth = 8;	// we have to reduce the bitmap
631 				case 8 :
632 					mbGrayScale = sal_True;
633 					break;
634 				default :
635 					return sal_False;
636 			}
637 		}
638 		break;
639 
640 		case 6 :	// each pixel is an RGB triple followed by an alpha sample
641 		{
642 			mbRGBTriple = sal_True;
643 			nScansize64 *= 4;
644 			mbAlphaChannel = sal_True;
645 			switch (mnPngDepth )
646 			{
647 				case 16 :			// we have to reduce the bitmap
648 				case 8 :
649 					mnTargetDepth = 24;
650 					break;
651 				default :
652 					return sal_False;
653 			}
654 		}
655 		break;
656 
657 		default :
658 			return sal_False;
659 	}
660 
661     mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() );
662     if ( !mnBPP )
663         mnBPP = 1;
664 
665     nScansize64++;       // each scanline includes one filterbyte
666 
667 	if ( nScansize64 > SAL_MAX_UINT32 )
668 		return sal_False;
669 
670 	mnScansize = static_cast< sal_uInt32 >( nScansize64 );
671 
672     // TODO: switch between both scanlines instead of copying
673 	mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ];
674     mpScanCurrent = mpInflateInBuf;
675 	mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ];
676 
677 	if ( !mpInflateInBuf || !mpScanPrior )
678 		return sal_False;
679 
680     // calculate target size from original size and the preview hint
681     if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() )
682     {
683 		Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() );
684         maTargetSize = maOrigSize;
685 
686 		if( aPreviewSize.Width() == 0 ) {
687 			aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() );
688 			if( aPreviewSize.Width() <= 0 )
689 				aPreviewSize.setWidth( 1 );
690 		} else if( aPreviewSize.Height() == 0 ) {
691 			aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() );
692 			if( aPreviewSize.Height() <= 0 )
693 				aPreviewSize.setHeight( 1 );
694 		}
695 
696 		if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) {
697 			OSL_TRACE("preview size %dx%d", aPreviewSize.Width(), aPreviewSize.Height() );
698 
699 			for( int i = 1; i < 5; ++i )
700 				{
701 					if( (maTargetSize.Width() >> i) < aPreviewSize.Width() )
702 						break;
703 					if( (maTargetSize.Height() >> i) < aPreviewSize.Height() )
704 						break;
705 					mnPreviewShift = i;
706 				}
707 			mnPreviewMask = (1 << mnPreviewShift) - 1;
708 		}
709     }
710 
711     maTargetSize.Width()  = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift;
712     maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift;
713 
714     mpBmp = new Bitmap( maTargetSize, mnTargetDepth );
715     mpAcc = mpBmp->AcquireWriteAccess();
716     if( !mpAcc )
717         return sal_False;
718 
719     mpBmp->SetSourceSizePixel( maOrigSize );
720 
721     if ( mbAlphaChannel )
722     {
723         mpAlphaMask = new AlphaMask( maTargetSize );
724         mpAlphaMask->Erase( 128 );
725         mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
726         if( !mpMaskAcc )
727             return sal_False;
728     }
729 
730 	if ( mbGrayScale )
731 		ImplGetGrayPalette( mnPngDepth );
732 
733     ImplPreparePass();
734 
735 	return sal_True;
736 }
737 
738 // ------------------------------------------------------------------------
739 
ImplGetGrayPalette(sal_uInt16 nBitDepth)740 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth )
741 {
742     if( nBitDepth > 8 )
743         nBitDepth = 8;
744 
745     sal_uInt16  nPaletteEntryCount = 1 << nBitDepth;
746 	sal_uInt32  nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0;
747 
748     // no bitdepth==2 available
749     // but bitdepth==4 with two unused bits is close enough
750     if( nBitDepth == 2 )
751         nPaletteEntryCount = 16;
752 
753 	mpAcc->SetPaletteEntryCount( nPaletteEntryCount );
754 	for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd )
755 		mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ],
756 			mpColorTable[ nStart ], mpColorTable[ nStart ] ) );
757 }
758 
759 // ------------------------------------------------------------------------
760 
ImplReadPalette()761 sal_Bool PNGReaderImpl::ImplReadPalette()
762 {
763 	sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 );
764 
765 	if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc )
766 	{
767 		mbPalette = sal_True;
768 		mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount );
769 
770 		for ( sal_uInt16 i = 0; i < nCount; i++ )
771 		{
772 			sal_uInt8 nRed =   mpColorTable[ *maDataIter++ ];
773 			sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ];
774 			sal_uInt8 nBlue =  mpColorTable[ *maDataIter++ ];
775 			mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) );
776 		}
777 	}
778 	else
779 		mbStatus = sal_False;
780 
781 	return mbStatus;
782 }
783 
784 // ------------------------------------------------------------------------
785 
ImplReadTransparent()786 sal_Bool PNGReaderImpl::ImplReadTransparent()
787 {
788     bool bNeedAlpha = false;
789 
790 	if ( mpTransTab == NULL )
791 	{
792 		switch ( mnColorType )
793 		{
794 			case 0 :
795 			{
796 				if ( mnChunkLen == 2 )
797 				{
798 					mpTransTab = new sal_uInt8[ 256 ];
799 					rtl_fillMemory( mpTransTab, 256, 0xff );
800                     // color type 0 and 4 is always greyscale,
801                     // so the return value can be used as index
802 					sal_uInt8 nIndex = ImplScaleColor();
803 					mpTransTab[ nIndex ] = 0;
804 					mbTransparent = true;
805 				}
806 			}
807 			break;
808 
809 			case 2 :
810 			{
811 				if ( mnChunkLen == 6 )
812 				{
813 					mnTransRed = ImplScaleColor();
814 					mnTransGreen = ImplScaleColor();
815 					mnTransBlue = ImplScaleColor();
816 					mbTransparent = true;
817 				}
818 			}
819 			break;
820 
821 			case 3 :
822 			{
823 				if ( mnChunkLen <= 256 )
824 				{
825 					mpTransTab = new sal_uInt8 [ 256 ];
826 					rtl_fillMemory( mpTransTab, 256, 0xff );
827 					rtl_copyMemory( mpTransTab, &(*maDataIter), mnChunkLen );
828 					maDataIter += mnChunkLen;
829 					mbTransparent = true;
830 					// need alpha transparency if not on/off masking
831 					for( int i = 0; i < mnChunkLen; ++i )
832 					   bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF);
833 				}
834 			}
835 			break;
836 		}
837 	}
838 
839     if( mbTransparent && !mbAlphaChannel && !mpMaskBmp )
840     {
841         if( bNeedAlpha)
842         {
843             mpAlphaMask = new AlphaMask( maTargetSize );
844             mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
845         }
846         else
847         {
848             mpMaskBmp = new Bitmap( maTargetSize, 1 );
849             mpMaskAcc = mpMaskBmp->AcquireWriteAccess();
850         }
851         mbTransparent = (mpMaskAcc != NULL);
852         if( !mbTransparent )
853             return sal_False;
854         mcOpaqueColor = BitmapColor( 0x00 );
855         mcTranspColor = BitmapColor( 0xFF );
856         mpMaskAcc->Erase( 0x00 );
857     }
858 
859     return sal_True;
860 }
861 
862 // ------------------------------------------------------------------------
863 
ImplGetGamma()864 void PNGReaderImpl::ImplGetGamma()
865 {
866 	if( mnChunkLen < 4 )
867 	    return;
868 
869 	sal_uInt32	nGammaValue = ImplReadsal_uInt32();
870 	double		fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) );
871 	double 		fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
872 
873 	if ( fInvGamma != 1.0 )
874 	{
875 		mbGamma = sal_True;
876 
877 		if ( mpColorTable == mpDefaultColorTable )
878 			mpColorTable = new sal_uInt8[ 256 ];
879 
880 		for ( sal_Int32 i = 0; i < 256; i++ )
881 			mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5);
882 
883 		if ( mbGrayScale )
884 			ImplGetGrayPalette( mnPngDepth );
885 	}
886 }
887 
888 // ------------------------------------------------------------------------
889 
ImplGetBackground()890 void PNGReaderImpl::ImplGetBackground()
891 {
892 	switch ( mnColorType )
893 	{
894 		case 3 :
895 		{
896 			if ( mnChunkLen == 1 )
897 			{
898 				sal_uInt16 nCol = *maDataIter++;
899 				if ( nCol < mpAcc->GetPaletteEntryCount() )
900 				{
901 					mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) );
902 					break;
903 				}
904 			}
905 		}
906 		break;
907 
908 		case 0 :
909 		case 4 :
910 		{
911 			if ( mnChunkLen == 2 )
912 			{
913                 // the color type 0 and 4 is always greyscale,
914                 // so the return value can be used as index
915 				sal_uInt8 nIndex = ImplScaleColor();
916 				mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) );
917 			}
918 		}
919 		break;
920 
921 		case 2 :
922 		case 6 :
923 		{
924 			if ( mnChunkLen == 6 )
925 			{
926 				sal_uInt8 nRed = ImplScaleColor();
927 				sal_uInt8 nGreen = ImplScaleColor();
928 				sal_uInt8 nBlue = ImplScaleColor();
929 				mpAcc->Erase( Color( nRed, nGreen, nBlue ) );
930 			}
931 		}
932 		break;
933 	}
934 }
935 
936 // ------------------------------------------------------------------------
937 
938 // for color type 0 and 4 (greyscale) the return value is always index to the color
939 //                2 and 6 (RGB)       the return value is always the 8 bit color component
ImplScaleColor()940 sal_uInt8 PNGReaderImpl::ImplScaleColor()
941 {
942     sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 );
943 	sal_uInt16 nCol = ( *maDataIter++ << 8 );
944 
945 	nCol += *maDataIter++ & (sal_uInt16)nMask;
946 
947 	if ( mnPngDepth > 8 )	// convert 16bit graphics to 8
948 		nCol >>= 8;
949 
950 	return (sal_uInt8) nCol;
951 }
952 
953 // ------------------------------------------------------------------------
954 // ImplReadIDAT reads as much image data as needed
955 
ImplReadIDAT()956 void PNGReaderImpl::ImplReadIDAT()
957 {
958 	if( mnChunkLen > 0 )
959 	{
960 		if ( mbzCodecInUse == sal_False )
961 		{
962 			mbzCodecInUse = sal_True;
963 			mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT );
964 		}
965 		mpZCodec->SetBreak( mnChunkLen );
966 		SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, STREAM_READ );
967 
968 		while ( ( mpZCodec->GetBreak() ) )
969 		{
970             // get bytes needed to fill the current scanline
971             sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf);
972             sal_Int32 nRead = mpZCodec->ReadAsynchron( aIStrm, mpScanCurrent, nToRead );
973             if ( nRead < 0 )
974 			{
975 				mbStatus = sal_False;
976 				break;
977 			}
978 			if ( nRead < nToRead )
979 			{
980 				mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk
981 				break;
982 			}
983 			else  // this scanline is Finished
984 			{
985 				mpScanCurrent = mpInflateInBuf;
986                 ImplApplyFilter();
987 
988                 ImplDrawScanline( mnXStart, mnXAdd );
989                 mnYpos += mnYAdd;
990 			}
991 
992 			if ( mnYpos >= (sal_uInt32)maOrigSize.Height() )
993 			{
994                 if( (mnPass < 7) && mnInterlaceType )
995                     if( ImplPreparePass() )
996                         continue;
997                 mbIDAT = true;
998                 break;
999 			}
1000 		}
1001 	}
1002 
1003 	if( mbIDAT )
1004 	{
1005 		mpZCodec->EndCompression();
1006 		mbzCodecInUse = sal_False;
1007 	}
1008 }
1009 
1010 // ---------------------------------------------------------------------------------------------------
1011 
ImplPreparePass()1012 bool PNGReaderImpl::ImplPreparePass()
1013 {
1014     struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; };
1015     static const InterlaceParams aInterlaceParams[8] =
1016     {
1017         // non-interlaced
1018         { 0, 0, 1, 1 },
1019         // Adam7-interlaced
1020         { 0, 0, 8, 8 },    // pass 1
1021         { 4, 0, 8, 8 },    // pass 2
1022         { 0, 4, 4, 8 },    // pass 3
1023         { 2, 0, 4, 4 },    // pass 4
1024         { 0, 2, 2, 4 },    // pass 5
1025         { 1, 0, 2, 2 },    // pass 6
1026         { 0, 1, 1, 2 }     // pass 7
1027     };
1028 
1029     const InterlaceParams* pParam = &aInterlaceParams[ 0 ];
1030     if( mnInterlaceType )
1031     {
1032         while( ++mnPass <= 7 )
1033         {
1034             pParam = &aInterlaceParams[ mnPass ];
1035 
1036             // skip this pass if the original image is too small for it
1037             if( (pParam->mnXStart < maOrigSize.Width())
1038             &&  (pParam->mnYStart < maOrigSize.Height()) )
1039                 break;
1040         }
1041         if( mnPass > 7 )
1042             return false;
1043 
1044         // skip the last passes if possible (for scaled down target images)
1045         if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) )
1046             return false;
1047     }
1048 
1049     mnYpos      = pParam->mnYStart;
1050     mnXStart    = pParam->mnXStart;
1051     mnXAdd      = pParam->mnXAdd;
1052     mnYAdd      = pParam->mnYAdd;
1053 
1054     // in Interlace mode the size of scanline is not constant
1055     // so first we calculate the number of entrys
1056     long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd;
1057     mnScansize = nScanWidth;
1058 
1059     if( mbRGBTriple )
1060         mnScansize = 3 * nScanWidth;
1061 
1062     if( mbAlphaChannel )
1063         mnScansize += nScanWidth;
1064 
1065     // convert to width in bytes
1066     mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3;
1067 
1068     ++mnScansize; // scan size also needs room for the filtertype byte
1069     rtl_zeroMemory( mpScanPrior, mnScansize );
1070 
1071     return true;
1072 }
1073 
1074 // ----------------------------------------------------------------------------
1075 // ImplApplyFilter writes the complete Scanline (nY)
1076 // in interlace mode the parameter nXStart and nXAdd are non-zero
1077 
ImplApplyFilter()1078 void PNGReaderImpl::ImplApplyFilter()
1079 {
1080 	OSL_ASSERT( mnScansize >= mnBPP + 1 );
1081     const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize;
1082 
1083     sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline
1084     switch ( nFilterType )
1085     {
1086         default: // unknown Scanline Filter Type
1087         case 0: // Filter Type "None"
1088             // we let the pixels pass and display the data unfiltered
1089             break;
1090 
1091         case 1: // Scanline Filter Type "Sub"
1092         {
1093             sal_uInt8* p1 = mpInflateInBuf + 1;
1094             const sal_uInt8* p2 = p1;
1095             p1 += mnBPP;
1096 
1097             // use left pixels
1098             do
1099                 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1100             while( ++p1 < pScanEnd );
1101         }
1102         break;
1103 
1104         case 2: // Scanline Filter Type "Up"
1105         {
1106             sal_uInt8* p1 = mpInflateInBuf + 1;
1107             const sal_uInt8* p2 = mpScanPrior + 1;
1108 
1109             // use pixels from prior line
1110 			while( p1 < pScanEnd )
1111 			{
1112                 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1113 				++p1;
1114 			}
1115         }
1116         break;
1117 
1118         case 3: // Scanline Filter Type "Average"
1119         {
1120             sal_uInt8* p1 = mpInflateInBuf + 1;
1121             const sal_uInt8* p2 = mpScanPrior + 1;
1122             const sal_uInt8* p3 = p1;
1123 
1124             // use one pixel from prior line
1125             for( int n = mnBPP; --n >= 0; ++p1, ++p2)
1126                 *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) );
1127 
1128             // predict by averaging the left and prior line pixels
1129 			while( p1 < pScanEnd )
1130 			{
1131                 *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) );
1132 				++p1;
1133 			}
1134         }
1135         break;
1136 
1137         case 4: // Scanline Filter Type "PaethPredictor"
1138         {
1139             sal_uInt8* p1 = mpInflateInBuf + 1;
1140             const sal_uInt8* p2 = mpScanPrior + 1;
1141             const sal_uInt8* p3 = p1;
1142             const sal_uInt8* p4 = p2;
1143 
1144             // use one pixel from prior line
1145             for( int n = mnBPP; --n >= 0; ++p1)
1146                 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1147 
1148             // predict by using the left and the prior line pixels
1149             while( p1 < pScanEnd )
1150             {
1151                 int na = *(p2++);
1152                 int nb = *(p3++);
1153                 int nc = *(p4++);
1154 
1155                 int npa = nb - (int)nc;
1156                 int npb = na - (int)nc;
1157                 int npc = npa + npb;
1158 
1159                 if( npa < 0 )
1160                     npa =-npa;
1161                 if( npb < 0 )
1162                     npb =-npb;
1163                 if( npc < 0 )
1164                     npc =-npc;
1165 
1166                 if( npa > npb )
1167                     na = nb, npa = npb;
1168                 if( npa > npc )
1169                     na = nc;
1170 
1171                 *p1 = static_cast<sal_uInt8>( *p1 + na );
1172 				++p1;
1173 			}
1174         }
1175         break;
1176     }
1177 
1178     rtl_copyMemory( mpScanPrior, mpInflateInBuf, mnScansize );
1179 }
1180 
1181 // ---------------------------------------------------------------------------------------------------
1182 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1183 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1184 
ImplDrawScanline(sal_uInt32 nXStart,sal_uInt32 nXAdd)1185 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd )
1186 {
1187     // optimization for downscaling
1188     if( mnYpos & mnPreviewMask )
1189         return;
1190     if( nXStart & mnPreviewMask )
1191         return;
1192 
1193     // convert nY to pixel units in the target image
1194     // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1195     const sal_uInt32 nY = mnYpos >> mnPreviewShift;
1196 
1197     const sal_uInt8* pTmp = mpInflateInBuf + 1;
1198 	if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1199 	{
1200 		switch ( mpAcc->GetBitCount() )
1201 		{
1202 			case 1 :
1203 			{
1204 				if ( mbTransparent )
1205 				{
1206                     for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1207 					{
1208                         sal_uInt8 nCol;
1209                         nShift = (nShift - 1) & 7;
1210 						if ( nShift == 0 )
1211 							nCol = *(pTmp++);
1212 						else
1213 							nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1214                         nCol &= 1;
1215 
1216 						ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1217 					}
1218 				}
1219 				else
1220 				{   // BMP_FORMAT_1BIT_MSB_PAL
1221 					for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1222 					{
1223                         nShift = (nShift - 1) & 7;
1224 
1225                         sal_uInt8 nCol;
1226 						if ( nShift == 0 )
1227 							nCol = *(pTmp++);
1228 						else
1229 							nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1230                         nCol &= 1;
1231 
1232                         ImplSetPixel( nY, nX, nCol );
1233 					}
1234 				}
1235 			}
1236 			break;
1237 
1238 			case 4 :
1239 			{
1240 				if ( mbTransparent )
1241 				{
1242 					if ( mnPngDepth == 4 )	// check if source has a two bit pixel format
1243 					{
1244 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex )
1245 						{
1246 							if( nXIndex & 1 )
1247 							{
1248 								ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] );
1249 								pTmp++;
1250 							}
1251 							else
1252 							{
1253 								ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] );
1254 							}
1255 						}
1256 					}
1257 					else // if ( mnPngDepth == 2 )
1258 					{
1259 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1260 						{
1261                             sal_uInt8 nCol;
1262 							switch( nXIndex & 3 )
1263 							{
1264 								case 0 :
1265 									nCol = *pTmp >> 6;
1266 								break;
1267 
1268 								case 1 :
1269 									nCol = ( *pTmp >> 4 ) & 0x03 ;
1270 								break;
1271 
1272 								case 2 :
1273 									nCol = ( *pTmp >> 2 ) & 0x03;
1274 								break;
1275 
1276 								case 3 :
1277 									nCol = ( *pTmp++ ) & 0x03;
1278 								break;
1279 
1280                                 default:    // get rid of nCol uninitialized warning
1281                                     nCol = 0;
1282                                     break;
1283 							}
1284 
1285 							ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1286 						}
1287 					}
1288 				}
1289 				else
1290 				{
1291 					if ( mnPngDepth == 4 )	// maybe the source is a two bitmap graphic
1292 					{   // BMP_FORMAT_4BIT_LSN_PAL
1293 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1294 						{
1295 							if( nXIndex & 1 )
1296 								ImplSetPixel( nY, nX, *pTmp++ & 0x0f );
1297 							else
1298 								ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f );
1299 						}
1300 					}
1301 					else // if ( mnPngDepth == 2 )
1302 					{
1303 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1304 						{
1305 							switch( nXIndex & 3 )
1306 							{
1307 								case 0 :
1308 									ImplSetPixel( nY, nX, *pTmp >> 6 );
1309 								break;
1310 
1311 								case 1 :
1312 									ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 );
1313 								break;
1314 
1315 								case 2 :
1316 									ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 );
1317 								break;
1318 
1319 								case 3 :
1320 									ImplSetPixel( nY, nX, *pTmp++ & 0x03 );
1321 								break;
1322 							}
1323 						}
1324 					}
1325 				}
1326 			}
1327 			break;
1328 
1329 			case 8 :
1330 			{
1331 				if ( mbAlphaChannel )
1332 				{
1333 					if ( mnPngDepth == 8 )	// maybe the source is a 16 bit grayscale
1334 					{
1335 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1336 							ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] );
1337 					}
1338 					else
1339 					{
1340 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1341 							ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] );
1342 					}
1343 				}
1344 				else if ( mbTransparent )
1345 				{
1346 					if ( mnPngDepth == 8 )	// maybe the source is a 16 bit grayscale
1347 					{
1348 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ )
1349 							ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1350 					}
1351 					else
1352 					{
1353 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1354 							ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1355 					}
1356 				}
1357 				else // neither alpha nor transparency
1358 				{
1359 					if ( mnPngDepth == 8 )	// maybe the source is a 16 bit grayscale
1360 					{
1361                         if( nXAdd == 1 && mnPreviewShift == 0 )  // copy raw line data if possible
1362                         {
1363                             int nLineBytes = maOrigSize.Width();
1364                             mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes );
1365                             pTmp += nLineBytes;
1366                         }
1367                         else
1368                         {
1369                             for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd )
1370                                 ImplSetPixel( nY, nX, *pTmp++ );
1371                         }
1372 					}
1373 					else
1374 					{
1375 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1376 							ImplSetPixel( nY, nX, *pTmp );
1377 					}
1378 				}
1379 			}
1380 			break;
1381 
1382 			default :
1383 				mbStatus = sal_False;
1384 			break;
1385 		}
1386 	}
1387 	else // no palette => truecolor
1388 	{
1389         // #122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
1390         static bool bCkeckDirectScanline(true);
1391 
1392         if( mbAlphaChannel )
1393         {
1394             // has RGB + alpha
1395             if ( mnPngDepth == 8 )  // maybe the source has 16 bit per sample
1396             {
1397                 // BMP_FORMAT_32BIT_TC_RGBA
1398                 // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha
1399                 const bool bDoDirectScanline(
1400                     bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpAcc && mpMaskAcc);
1401                 const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1402 
1403                 if(bDoDirectScanline)
1404                 {
1405                     // allocate scanlines on demand, reused for next line
1406                     if(!mpScanline)
1407                     {
1408 #ifdef DBG_UTIL
1409                         mnAllocSizeScanline = maOrigSize.Width() * 3;
1410 #endif
1411                         mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1412                     }
1413 
1414                     if(!mpScanlineAlpha)
1415                     {
1416 #ifdef DBG_UTIL
1417                         mnAllocSizeScanlineAlpha = maOrigSize.Width();
1418 #endif
1419                         mpScanlineAlpha = new sal_uInt8[maOrigSize.Width()];
1420                     }
1421                 }
1422 
1423                 if(bDoDirectScanline)
1424                 {
1425                     OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1426                     OSL_ENSURE(mpScanlineAlpha, "No ScanlineAlpha allocated (!)");
1427 #ifdef DBG_UTIL
1428                     OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1429                     OSL_ENSURE(mnAllocSizeScanlineAlpha >= maOrigSize.Width(), "Allocated ScanlineAlpha too small (!)");
1430 #endif
1431                     sal_uInt8* pScanline(mpScanline);
1432                     sal_uInt8* pScanlineAlpha(mpScanlineAlpha);
1433 
1434                     for(sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 4)
1435                     {
1436                         // prepare content line as BGR by reordering when copying
1437                         // do not forget to invert alpha (source is alpha, target is opacity)
1438                         if(bCustomColorTable)
1439                         {
1440                             *pScanline++ = mpColorTable[pTmp[2]];
1441                             *pScanline++ = mpColorTable[pTmp[1]];
1442                             *pScanline++ = mpColorTable[pTmp[0]];
1443                             *pScanlineAlpha++ = ~pTmp[3];
1444                         }
1445                         else
1446                         {
1447                             *pScanline++ = pTmp[2];
1448                             *pScanline++ = pTmp[1];
1449                             *pScanline++ = pTmp[0];
1450                             *pScanlineAlpha++ = ~pTmp[3];
1451                         }
1452                     }
1453 
1454                     // copy scanlines directly to bitmaps for content and alpha; use the formats which
1455                     // are able to copy directly to BitmapBuffer
1456                     mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3);
1457                     mpMaskAcc->CopyScanline(nY, mpScanlineAlpha, BMP_FORMAT_8BIT_PAL, maOrigSize.Width());
1458                 }
1459                 else
1460                 {
1461                     for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1462                     {
1463                         if(bCustomColorTable)
1464                         {
1465                             ImplSetAlphaPixel(
1466                                 nY,
1467                                 nX,
1468                                 BitmapColor(
1469                                     mpColorTable[ pTmp[ 0 ] ],
1470                                     mpColorTable[ pTmp[ 1 ] ],
1471                                     mpColorTable[ pTmp[ 2 ] ]),
1472                                 pTmp[ 3 ]);
1473                         }
1474                         else
1475                         {
1476                             ImplSetAlphaPixel(
1477                                 nY,
1478                                 nX,
1479                                 BitmapColor(
1480                                     pTmp[0],
1481                                     pTmp[1],
1482                                     pTmp[2]),
1483                                 pTmp[3]);
1484                         }
1485                     }
1486                 }
1487             }
1488             else
1489             {
1490                 // BMP_FORMAT_64BIT_TC_RGBA
1491                 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 )
1492                 {
1493                     ImplSetAlphaPixel(
1494                         nY,
1495                         nX,
1496                         BitmapColor(
1497                             mpColorTable[ pTmp[ 0 ] ],
1498                             mpColorTable[ pTmp[ 2 ] ],
1499                             mpColorTable[ pTmp[ 4 ] ]),
1500                         pTmp[6]);
1501                 }
1502             }
1503         }
1504         else if( mbTransparent ) // has RGB + transparency
1505         {
1506             // BMP_FORMAT_24BIT_TC_RGB
1507             // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1508             if ( mnPngDepth == 8 )  // maybe the source has 16 bit per sample
1509             {
1510                 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1511                 {
1512                     sal_uInt8 nRed = pTmp[ 0 ];
1513                     sal_uInt8 nGreen = pTmp[ 1 ];
1514                     sal_uInt8 nBlue = pTmp[ 2 ];
1515                     sal_Bool bTransparent = ( ( nRed == mnTransRed )
1516                                         && ( nGreen == mnTransGreen )
1517                                         && ( nBlue == mnTransBlue ) );
1518 
1519                     ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1520                                                         mpColorTable[ nGreen ],
1521                                                         mpColorTable[ nBlue ] ), bTransparent );
1522                 }
1523             }
1524             else
1525             {
1526                 // BMP_FORMAT_48BIT_TC_RGB
1527                 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1528                 {
1529                     sal_uInt8 nRed = pTmp[ 0 ];
1530                     sal_uInt8 nGreen = pTmp[ 2 ];
1531                     sal_uInt8 nBlue = pTmp[ 4 ];
1532                     sal_Bool bTransparent = ( ( nRed == mnTransRed )
1533                                         && ( nGreen == mnTransGreen )
1534                                         && ( nBlue == mnTransBlue ) );
1535 
1536                     ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1537                                                         mpColorTable[ nGreen ],
1538                                                         mpColorTable[ nBlue ] ), bTransparent );
1539                 }
1540             }
1541         }
1542         else  // has RGB but neither alpha nor transparency
1543         {
1544             // BMP_FORMAT_24BIT_TC_RGB
1545             // only use DirectScanline when we have no preview shifting stuff and access to content
1546             const bool bDoDirectScanline(
1547                 bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpAcc);
1548             const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1549 
1550             if(bDoDirectScanline && !mpScanline)
1551             {
1552                 // allocate scanlines on demand, reused for next line
1553 #ifdef DBG_UTIL
1554                 mnAllocSizeScanline = maOrigSize.Width() * 3;
1555 #endif
1556                 mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1557             }
1558 
1559             if ( mnPngDepth == 8 )   // maybe the source has 16 bit per sample
1560             {
1561                 if(bDoDirectScanline)
1562                 {
1563                     OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1564 #ifdef DBG_UTIL
1565                     OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1566 #endif
1567                     sal_uInt8* pScanline(mpScanline);
1568 
1569                     for(sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 3)
1570                     {
1571                         // prepare content line as BGR by reordering when copying
1572                         if(bCustomColorTable)
1573                         {
1574                             *pScanline++ = mpColorTable[pTmp[2]];
1575                             *pScanline++ = mpColorTable[pTmp[1]];
1576                             *pScanline++ = mpColorTable[pTmp[0]];
1577                         }
1578                         else
1579                         {
1580                             *pScanline++ = pTmp[2];
1581                             *pScanline++ = pTmp[1];
1582                             *pScanline++ = pTmp[0];
1583                         }
1584                     }
1585 
1586                     // copy scanline directly to bitmap for content; use the format which is able to
1587                     // copy directly to BitmapBuffer
1588                     mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3);
1589                 }
1590                 else
1591                 {
1592                     for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1593                     {
1594                         if(bCustomColorTable)
1595                         {
1596                             ImplSetPixel(
1597                                 nY,
1598                                 nX,
1599                                 BitmapColor(
1600                                     mpColorTable[ pTmp[ 0 ] ],
1601                                     mpColorTable[ pTmp[ 1 ] ],
1602                                     mpColorTable[ pTmp[ 2 ] ]));
1603                         }
1604                         else
1605                         {
1606                             ImplSetPixel(
1607                                 nY,
1608                                 nX,
1609                                 BitmapColor(
1610                                     pTmp[0],
1611                                     pTmp[1],
1612                                     pTmp[2]));
1613                         }
1614                     }
1615                 }
1616             }
1617             else
1618             {
1619                 // BMP_FORMAT_48BIT_TC_RGB
1620                 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1621                 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1622                 {
1623                     ImplSetPixel(
1624                         nY,
1625                         nX,
1626                         BitmapColor(
1627                             mpColorTable[ pTmp[ 0 ] ],
1628                             mpColorTable[ pTmp[ 2 ] ],
1629                             mpColorTable[ pTmp[ 4 ] ]));
1630                 }
1631             }
1632         }
1633     }
1634 }
1635 
1636 // ------------------------------------------------------------------------
1637 
ImplSetPixel(sal_uInt32 nY,sal_uInt32 nX,const BitmapColor & rBitmapColor)1638 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor )
1639 {
1640     // TODO: get preview mode checks out of inner loop
1641     if( nX & mnPreviewMask )
1642         return;
1643     nX >>= mnPreviewShift;
1644 
1645     mpAcc->SetPixel( nY, nX, rBitmapColor );
1646 }
1647 
1648 // ------------------------------------------------------------------------
1649 
ImplSetPixel(sal_uInt32 nY,sal_uInt32 nX,sal_uInt8 nPalIndex)1650 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex )
1651 {
1652     // TODO: get preview mode checks out of inner loop
1653     if( nX & mnPreviewMask )
1654         return;
1655     nX >>= mnPreviewShift;
1656 
1657     if (nPalIndex >= mpAcc->GetPaletteEntryCount())
1658         throw ::com::sun::star::lang::IndexOutOfBoundsException();
1659     mpAcc->SetPixelIndex( nY, nX, nPalIndex );
1660 }
1661 
1662 // ------------------------------------------------------------------------
1663 
ImplSetTranspPixel(sal_uInt32 nY,sal_uInt32 nX,const BitmapColor & rBitmapColor,sal_Bool bTrans)1664 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, sal_Bool bTrans )
1665 {
1666     // TODO: get preview mode checks out of inner loop
1667     if( nX & mnPreviewMask )
1668         return;
1669     nX >>= mnPreviewShift;
1670 
1671     mpAcc->SetPixel( nY, nX, rBitmapColor );
1672 
1673     if ( bTrans )
1674         mpMaskAcc->SetPixel( nY, nX, mcTranspColor );
1675     else
1676         mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor );
1677 }
1678 
1679 // ------------------------------------------------------------------------
1680 
ImplSetAlphaPixel(sal_uInt32 nY,sal_uInt32 nX,sal_uInt8 nPalIndex,sal_uInt8 nAlpha)1681 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1682     sal_uInt8 nPalIndex, sal_uInt8 nAlpha )
1683 {
1684     // TODO: get preview mode checks out of inner loop
1685     if( nX & mnPreviewMask )
1686         return;
1687     nX >>= mnPreviewShift;
1688 
1689     if (nPalIndex >= mpAcc->GetPaletteEntryCount())
1690         throw ::com::sun::star::lang::IndexOutOfBoundsException();
1691     mpAcc->SetPixelIndex( nY, nX, nPalIndex );
1692     mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha );
1693 }
1694 
1695 // ------------------------------------------------------------------------
1696 
ImplSetAlphaPixel(sal_uInt32 nY,sal_uInt32 nX,const BitmapColor & rBitmapColor,sal_uInt8 nAlpha)1697 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1698     const BitmapColor& rBitmapColor, sal_uInt8 nAlpha )
1699 {
1700     // TODO: get preview mode checks out of inner loop
1701     if( nX & mnPreviewMask )
1702         return;
1703     nX >>= mnPreviewShift;
1704 
1705     mpAcc->SetPixel( nY, nX, rBitmapColor );
1706     mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha );
1707 }
1708 
1709 // ------------------------------------------------------------------------
1710 
ImplReadsal_uInt32()1711 sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32()
1712 {
1713 	sal_uInt32 nRet;
1714 	nRet = *maDataIter++;
1715 	nRet <<= 8;
1716 	nRet |= *maDataIter++;
1717 	nRet <<= 8;
1718 	nRet |= *maDataIter++;
1719 	nRet <<= 8;
1720 	nRet |= *maDataIter++;
1721 	return nRet;
1722 }
1723 
1724 // ------------------------------------------------------------------------
1725 
1726 // -------------
1727 // - PNGReader -
1728 // -------------
1729 
PNGReader(SvStream & rIStm)1730 PNGReader::PNGReader( SvStream& rIStm ) :
1731 	mpImpl( new ::vcl::PNGReaderImpl( rIStm ) )
1732 {
1733 }
1734 
1735 // ------------------------------------------------------------------------
1736 
~PNGReader()1737 PNGReader::~PNGReader()
1738 {
1739 	delete mpImpl;
1740 }
1741 
1742 // ------------------------------------------------------------------------
1743 
Read(const Size & i_rPreviewSizeHint)1744 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint )
1745 {
1746     return mpImpl->GetBitmapEx( i_rPreviewSizeHint );
1747 }
1748 
1749 // ------------------------------------------------------------------------
1750 
GetChunks() const1751 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const
1752 {
1753 	return mpImpl->GetAllChunks();
1754 }
1755 
1756 // ------------------------------------------------------------------------
1757 
SetIgnoreGammaChunk(sal_Bool b)1758 void PNGReader::SetIgnoreGammaChunk( sal_Bool b )
1759 {
1760 	mpImpl->SetIgnoreGammaChunk( b );
1761 }
1762 
1763 
1764 } // namespace vcl
1765