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