xref: /trunk/main/vcl/source/gdi/pngread.cxx (revision 9f62ea84)
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 	mbzCodecInUse	( sal_False ),
201     mbStatus( sal_True),
202     mbIDAT( sal_False ),
203 	mbGamma				( sal_False ),
204 	mbpHYs				( sal_False ),
205 	mbIgnoreGammaChunk	( sal_False )
206 {
207     // prepare the PNG data stream
208     mnOrigStreamMode = mrPNGStream.GetNumberFormatInt();
209     mrPNGStream.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
210 
211     // prepare the chunk reader
212     maChunkSeq.reserve( 16 );
213     maChunkIter = maChunkSeq.begin();
214 
215     // estimate PNG file size (to allow sanity checks)
216     const sal_Size nStreamPos = mrPNGStream.Tell();
217     mrPNGStream.Seek( STREAM_SEEK_TO_END );
218     mnStreamSize = mrPNGStream.Tell();
219     mrPNGStream.Seek( nStreamPos );
220 
221     // check the PNG header magic
222     sal_uInt32 nDummy = 0;
223     mrPNGStream >> nDummy;
224     mbStatus = (nDummy == 0x89504e47);
225     mrPNGStream >> nDummy;
226     mbStatus &= (nDummy == 0x0d0a1a0a);
227 
228     mnPreviewShift = 0;
229     mnPreviewMask = (1 << mnPreviewShift) - 1;
230 }
231 
232 // ------------------------------------------------------------------------
233 
234 PNGReaderImpl::~PNGReaderImpl()
235 {
236     mrPNGStream.SetNumberFormatInt( mnOrigStreamMode );
237 
238 	if ( mbzCodecInUse )
239 		mpZCodec->EndCompression();
240 
241 	if( mpColorTable != mpDefaultColorTable )
242 		delete[] mpColorTable;
243 
244     delete mpBmp;
245     delete mpAlphaMask;
246     delete mpMaskBmp;
247     delete[] mpTransTab;
248     delete[] mpInflateInBuf;
249     delete[] mpScanPrior;
250     delete mpZCodec;
251 }
252 
253 // ------------------------------------------------------------------------
254 
255 bool PNGReaderImpl::ReadNextChunk()
256 {
257     if( maChunkIter == maChunkSeq.end() )
258     {
259         // get the next chunk from the stream
260 
261         // unless we are at the end of the PNG stream
262         if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) )
263             return false;
264         if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
265             return false;
266 
267         PNGReader::ChunkData aDummyChunk;
268         maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk );
269         PNGReader::ChunkData& rChunkData = *maChunkIter;
270 
271         // read the chunk header
272         mrPNGStream >> mnChunkLen >> mnChunkType;
273         rChunkData.nType = mnChunkType;
274 
275         // #128377#/#149343# sanity check for chunk length
276         if( mnChunkLen < 0 )
277             return false;
278         const sal_Size nStreamPos = mrPNGStream.Tell();
279         if( nStreamPos + mnChunkLen >= mnStreamSize )
280             return false;
281 
282         // calculate chunktype CRC (swap it back to original byte order)
283         sal_uInt32 nChunkType = mnChunkType;;
284         #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
285         nChunkType = SWAPLONG( nChunkType );
286         #endif
287         sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 );
288 
289         // read the chunk data and check the CRC
290         if( mnChunkLen && !mrPNGStream.IsEof() )
291         {
292             rChunkData.aData.resize( mnChunkLen );
293 
294             sal_Int32 nBytesRead = 0;
295             do {
296                 sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ];
297                 nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead );
298             } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) );
299 
300             nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen );
301             maDataIter = rChunkData.aData.begin();
302         }
303         sal_uInt32 nCheck;
304         mrPNGStream >> nCheck;
305         if( nCRC32 != nCheck )
306             return false;
307     }
308     else
309     {
310         // the next chunk was already read
311         mnChunkType = (*maChunkIter).nType;
312         mnChunkLen = (*maChunkIter).aData.size();
313         maDataIter = (*maChunkIter).aData.begin();
314     }
315 
316     ++maChunkIter;
317     if( mnChunkType == PNGCHUNK_IEND )
318         return false;
319     return true;
320 }
321 
322 // ------------------------------------------------------------------------
323 
324 // read the remaining chunks from mrPNGStream
325 void PNGReaderImpl::ReadRemainingChunks()
326 {
327     while( ReadNextChunk() ) ;
328 }
329 
330 // ------------------------------------------------------------------------
331 
332 // move position of mrPNGStream to the end of the file
333 void PNGReaderImpl::SkipRemainingChunks()
334 {
335     // nothing to skip if the last chunk was read
336     if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
337         return;
338 
339     // read from the stream until the IEND chunk is found
340     const sal_Size nStreamPos = mrPNGStream.Tell();
341     while( !mrPNGStream.IsEof() && (mrPNGStream.GetError() == ERRCODE_NONE) )
342     {
343         mrPNGStream >> mnChunkLen >> mnChunkType;
344         if( mnChunkLen < 0 )
345             break;
346         if( nStreamPos + mnChunkLen >= mnStreamSize )
347             break;
348         mrPNGStream.SeekRel( mnChunkLen + 4 );  // skip data + CRC
349         if( mnChunkType == PNGCHUNK_IEND )
350             break;
351     }
352 }
353 
354 // ------------------------------------------------------------------------
355 
356 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks()
357 {
358     ReadRemainingChunks();
359     return maChunkSeq;
360 }
361 
362 // ------------------------------------------------------------------------
363 
364 BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint )
365 {
366     // reset to the first chunk
367     maChunkIter = maChunkSeq.begin();
368 
369     // parse the chunks
370     while( mbStatus && !mbIDAT && ReadNextChunk() )
371     {
372         switch( mnChunkType )
373 		{
374 			case PNGCHUNK_IHDR :
375 			{
376 				mbStatus = ImplReadHeader( rPreviewSizeHint );
377 			}
378 			break;
379 
380 			case PNGCHUNK_gAMA :								// the gamma chunk must precede
381 			{													// the 'IDAT' and also the 'PLTE'(if available )
382 				if ( !mbIgnoreGammaChunk && ( mbIDAT == sal_False ) )
383 					ImplGetGamma();
384 			}
385 			break;
386 
387 			case PNGCHUNK_PLTE :
388 			{
389 				if ( !mbPalette )
390 					mbStatus = ImplReadPalette();
391 			}
392 			break;
393 
394 			case PNGCHUNK_tRNS :
395 			{
396 				if ( !mbIDAT )									// the tRNS chunk must precede the IDAT
397 					mbStatus = ImplReadTransparent();
398 			}
399 			break;
400 
401 			case PNGCHUNK_bKGD :								// the background chunk must appear
402 			{
403 				if ( ( mbIDAT == sal_False ) && mbPalette ) 		// before the 'IDAT' and after the
404 					ImplGetBackground();						// PLTE(if available ) chunk.
405 			}
406 			break;
407 
408 			case PNGCHUNK_IDAT :
409 			{
410 				if ( !mpInflateInBuf )	// taking care that the header has properly been read
411 					mbStatus = sal_False;
412 				else if ( !mbIDAT )		// the gfx is finished, but there may be left a zlibCRC of about 4Bytes
413 					ImplReadIDAT();
414 			}
415 			break;
416 
417 			case PNGCHUNK_pHYs :
418 			{
419 				if ( !mbIDAT && mnChunkLen == 9 )
420 				{
421                     sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32();
422                     sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32();
423 
424                     sal_uInt8 nUnitSpecifier = *maDataIter++;
425                     if( (nUnitSpecifier == 1) && nXPixelPerMeter && nXPixelPerMeter )
426                     {
427                         mbpHYs = sal_True;
428 
429                         // convert into MAP_100TH_MM
430                         maPhysSize.Width()  = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter );
431                         maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter );
432                     }
433 				}
434 			}
435 			break;
436 
437             case PNGCHUNK_IEND:
438                 mbStatus = mbIDAT;  // there is a problem if the image is not complete yet
439             break;
440 		}
441 	}
442 
443     // release write access of the bitmaps
444 	if ( mpAcc )
445 		mpBmp->ReleaseAccess( mpAcc ), mpAcc = NULL;
446 
447 	if ( mpMaskAcc )
448 	{
449 		if ( mpAlphaMask )
450 			mpAlphaMask->ReleaseAccess( mpMaskAcc );
451 		else if ( mpMaskBmp )
452 			mpMaskBmp->ReleaseAccess( mpMaskAcc );
453 
454 		mpMaskAcc = NULL;
455 	}
456 
457     // return the resulting BitmapEx
458     BitmapEx aRet;
459 
460     if( !mbStatus || !mbIDAT )
461         aRet.Clear();
462     else
463 	{
464 		if ( mpAlphaMask )
465 			aRet = BitmapEx( *mpBmp, *mpAlphaMask );
466 		else if ( mpMaskBmp )
467 			aRet = BitmapEx( *mpBmp, *mpMaskBmp );
468 		else
469 			aRet = *mpBmp;
470 
471 		if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() )
472 		{
473 			aRet.SetPrefMapMode( MAP_100TH_MM );
474 			aRet.SetPrefSize( maPhysSize );
475 		}
476 
477 #if 0
478         // TODO: make sure nobody depends on the stream being after the IEND chunks
479         // => let them do ReadChunks before
480         ReadRemainingChunks();
481 #endif
482 	}
483 
484 	return aRet;
485 }
486 
487 // ------------------------------------------------------------------------
488 
489 sal_Bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint )
490 {
491     if( mnChunkLen < 13 )
492         return sal_False;
493 
494     maOrigSize.Width()  = ImplReadsal_uInt32();
495     maOrigSize.Height() = ImplReadsal_uInt32();
496 
497     if ( !maOrigSize.Width() || !maOrigSize.Height() )
498         return sal_False;
499 
500     mnPngDepth = *(maDataIter++);
501     mnColorType = *(maDataIter++);
502 
503     mnCompressionType = *(maDataIter++);
504     if( mnCompressionType != 0 )    // unknown compression type
505         return sal_False;
506 
507     mnFilterType = *(maDataIter++);
508     if( mnFilterType != 0 )         // unknown filter type
509         return sal_False;
510 
511     mnInterlaceType = *(maDataIter++);
512     switch ( mnInterlaceType ) // filter type valid ?
513 	{
514 		case 0 :  // progressive image
515 			mnPass = 7;
516 			break;
517 		case 1 :  // Adam7-interlaced image
518 			mnPass = 0;
519 			break;
520 		default:
521 			return sal_False;
522 	}
523 
524 	mbPalette = sal_True;
525 	mbIDAT = mbAlphaChannel = mbTransparent = sal_False;
526 	mbGrayScale = mbRGBTriple = sal_False;
527 	mnTargetDepth = mnPngDepth;
528 	sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3;
529 
530 	// valid color types are 0,2,3,4 & 6
531 	switch ( mnColorType )
532 	{
533 		case 0 :	// each pixel is a grayscale
534 		{
535 			switch ( mnPngDepth )
536 			{
537 				case 2 : // 2bit target not available -> use four bits
538 					mnTargetDepth = 4;	// we have to expand the bitmap
539 					mbGrayScale = sal_True;
540 					break;
541 				case 16 :
542 					mnTargetDepth = 8;	// we have to reduce the bitmap
543 					// fall through
544 				case 1 :
545 				case 4 :
546 				case 8 :
547 					mbGrayScale = sal_True;
548 					break;
549 				default :
550 					return sal_False;
551 			}
552 		}
553 		break;
554 
555 		case 2 :	// each pixel is an RGB triple
556 		{
557 			mbRGBTriple = sal_True;
558 			nScansize64 *= 3;
559 			switch ( mnPngDepth )
560 			{
561 				case 16 :			// we have to reduce the bitmap
562 				case 8 :
563 					mnTargetDepth = 24;
564 					break;
565 				default :
566 					return sal_False;
567 			}
568 		}
569 		break;
570 
571 		case 3 :	// each pixel is a palette index
572 		{
573 			switch ( mnPngDepth )
574 			{
575 				case 2 :
576 					mnTargetDepth = 4;	// we have to expand the bitmap
577 					// fall through
578 				case 1 :
579 				case 4 :
580 				case 8 :
581 					mbPalette = sal_False;
582 					break;
583 				default :
584 					return sal_False;
585 			}
586 		}
587 		break;
588 
589 		case 4 :	// each pixel is a grayscale sample followed by an alpha sample
590 		{
591 			nScansize64 *= 2;
592 			mbAlphaChannel = sal_True;
593 			switch ( mnPngDepth )
594 			{
595 				case 16 :
596 					mnTargetDepth = 8;	// we have to reduce the bitmap
597 				case 8 :
598 					mbGrayScale = sal_True;
599 					break;
600 				default :
601 					return sal_False;
602 			}
603 		}
604 		break;
605 
606 		case 6 :	// each pixel is an RGB triple followed by an alpha sample
607 		{
608 			mbRGBTriple = sal_True;
609 			nScansize64 *= 4;
610 			mbAlphaChannel = sal_True;
611 			switch (mnPngDepth )
612 			{
613 				case 16 :			// we have to reduce the bitmap
614 				case 8 :
615 					mnTargetDepth = 24;
616 					break;
617 				default :
618 					return sal_False;
619 			}
620 		}
621 		break;
622 
623 		default :
624 			return sal_False;
625 	}
626 
627     mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() );
628     if ( !mnBPP )
629         mnBPP = 1;
630 
631     nScansize64++;       // each scanline includes one filterbyte
632 
633 	if ( nScansize64 > SAL_MAX_UINT32 )
634 		return sal_False;
635 
636 	mnScansize = static_cast< sal_uInt32 >( nScansize64 );
637 
638     // TODO: switch between both scanlines instead of copying
639 	mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ];
640     mpScanCurrent = mpInflateInBuf;
641 	mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ];
642 
643 	if ( !mpInflateInBuf || !mpScanPrior )
644 		return sal_False;
645 
646     // calculate target size from original size and the preview hint
647     if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() )
648     {
649 		Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() );
650         maTargetSize = maOrigSize;
651 
652 		if( aPreviewSize.Width() == 0 ) {
653 			aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() );
654 			if( aPreviewSize.Width() <= 0 )
655 				aPreviewSize.setWidth( 1 );
656 		} else if( aPreviewSize.Height() == 0 ) {
657 			aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() );
658 			if( aPreviewSize.Height() <= 0 )
659 				aPreviewSize.setHeight( 1 );
660 		}
661 
662 		if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) {
663 			OSL_TRACE("preview size %dx%d", aPreviewSize.Width(), aPreviewSize.Height() );
664 
665 			for( int i = 1; i < 5; ++i )
666 				{
667 					if( (maTargetSize.Width() >> i) < aPreviewSize.Width() )
668 						break;
669 					if( (maTargetSize.Height() >> i) < aPreviewSize.Height() )
670 						break;
671 					mnPreviewShift = i;
672 				}
673 			mnPreviewMask = (1 << mnPreviewShift) - 1;
674 		}
675     }
676 
677     maTargetSize.Width()  = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift;
678     maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift;
679 
680     mpBmp = new Bitmap( maTargetSize, mnTargetDepth );
681     mpAcc = mpBmp->AcquireWriteAccess();
682     if( !mpAcc )
683         return sal_False;
684 
685     mpBmp->SetSourceSizePixel( maOrigSize );
686 
687     if ( mbAlphaChannel )
688     {
689         mpAlphaMask = new AlphaMask( maTargetSize );
690         mpAlphaMask->Erase( 128 );
691         mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
692         if( !mpMaskAcc )
693             return sal_False;
694     }
695 
696 	if ( mbGrayScale )
697 		ImplGetGrayPalette( mnPngDepth );
698 
699     ImplPreparePass();
700 
701 	return sal_True;
702 }
703 
704 // ------------------------------------------------------------------------
705 
706 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth )
707 {
708     if( nBitDepth > 8 )
709         nBitDepth = 8;
710 
711     sal_uInt16  nPaletteEntryCount = 1 << nBitDepth;
712 	sal_uInt32  nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0;
713 
714     // no bitdepth==2 available
715     // but bitdepth==4 with two unused bits is close enough
716     if( nBitDepth == 2 )
717         nPaletteEntryCount = 16;
718 
719 	mpAcc->SetPaletteEntryCount( nPaletteEntryCount );
720 	for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd )
721 		mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ],
722 			mpColorTable[ nStart ], mpColorTable[ nStart ] ) );
723 }
724 
725 // ------------------------------------------------------------------------
726 
727 sal_Bool PNGReaderImpl::ImplReadPalette()
728 {
729 	sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 );
730 
731 	if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc )
732 	{
733 		mbPalette = sal_True;
734 		mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount );
735 
736 		for ( sal_uInt16 i = 0; i < nCount; i++ )
737 		{
738 			sal_uInt8 nRed =   mpColorTable[ *maDataIter++ ];
739 			sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ];
740 			sal_uInt8 nBlue =  mpColorTable[ *maDataIter++ ];
741 			mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) );
742 		}
743 	}
744 	else
745 		mbStatus = sal_False;
746 
747 	return mbStatus;
748 }
749 
750 // ------------------------------------------------------------------------
751 
752 sal_Bool PNGReaderImpl::ImplReadTransparent()
753 {
754     bool bNeedAlpha = false;
755 
756 	if ( mpTransTab == NULL )
757 	{
758 		switch ( mnColorType )
759 		{
760 			case 0 :
761 			{
762 				if ( mnChunkLen == 2 )
763 				{
764 					mpTransTab = new sal_uInt8[ 256 ];
765 					rtl_fillMemory( mpTransTab, 256, 0xff );
766                     // color type 0 and 4 is always greyscale,
767                     // so the return value can be used as index
768 					sal_uInt8 nIndex = ImplScaleColor();
769 					mpTransTab[ nIndex ] = 0;
770 					mbTransparent = true;
771 				}
772 			}
773 			break;
774 
775 			case 2 :
776 			{
777 				if ( mnChunkLen == 6 )
778 				{
779 					mnTransRed = ImplScaleColor();
780 					mnTransGreen = ImplScaleColor();
781 					mnTransBlue = ImplScaleColor();
782 					mbTransparent = true;
783 				}
784 			}
785 			break;
786 
787 			case 3 :
788 			{
789 				if ( mnChunkLen <= 256 )
790 				{
791 					mpTransTab = new sal_uInt8 [ 256 ];
792 					rtl_fillMemory( mpTransTab, 256, 0xff );
793 					rtl_copyMemory( mpTransTab, &(*maDataIter), mnChunkLen );
794 					maDataIter += mnChunkLen;
795 					mbTransparent = true;
796 					// need alpha transparency if not on/off masking
797 					for( int i = 0; i < mnChunkLen; ++i )
798 					   bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF);
799 				}
800 			}
801 			break;
802 		}
803 	}
804 
805     if( mbTransparent && !mbAlphaChannel && !mpMaskBmp )
806     {
807         if( bNeedAlpha)
808         {
809             mpAlphaMask = new AlphaMask( maTargetSize );
810             mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
811         }
812         else
813         {
814             mpMaskBmp = new Bitmap( maTargetSize, 1 );
815             mpMaskAcc = mpMaskBmp->AcquireWriteAccess();
816         }
817         mbTransparent = (mpMaskAcc != NULL);
818         if( !mbTransparent )
819             return sal_False;
820         mcOpaqueColor = BitmapColor( 0x00 );
821         mcTranspColor = BitmapColor( 0xFF );
822         mpMaskAcc->Erase( 0x00 );
823     }
824 
825     return sal_True;
826 }
827 
828 // ------------------------------------------------------------------------
829 
830 void PNGReaderImpl::ImplGetGamma()
831 {
832 	if( mnChunkLen < 4 )
833 	    return;
834 
835 	sal_uInt32	nGammaValue = ImplReadsal_uInt32();
836 	double		fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) );
837 	double 		fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
838 
839 	if ( fInvGamma != 1.0 )
840 	{
841 		mbGamma = sal_True;
842 
843 		if ( mpColorTable == mpDefaultColorTable )
844 			mpColorTable = new sal_uInt8[ 256 ];
845 
846 		for ( sal_Int32 i = 0; i < 256; i++ )
847 			mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5);
848 
849 		if ( mbGrayScale )
850 			ImplGetGrayPalette( mnPngDepth );
851 	}
852 }
853 
854 // ------------------------------------------------------------------------
855 
856 void PNGReaderImpl::ImplGetBackground()
857 {
858 	switch ( mnColorType )
859 	{
860 		case 3 :
861 		{
862 			if ( mnChunkLen == 1 )
863 			{
864 				sal_uInt16 nCol = *maDataIter++;
865 				if ( nCol < mpAcc->GetPaletteEntryCount() )
866 				{
867 					mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) );
868 					break;
869 				}
870 			}
871 		}
872 		break;
873 
874 		case 0 :
875 		case 4 :
876 		{
877 			if ( mnChunkLen == 2 )
878 			{
879                 // the color type 0 and 4 is always greyscale,
880                 // so the return value can be used as index
881 				sal_uInt8 nIndex = ImplScaleColor();
882 				mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) );
883 			}
884 		}
885 		break;
886 
887 		case 2 :
888 		case 6 :
889 		{
890 			if ( mnChunkLen == 6 )
891 			{
892 				sal_uInt8 nRed = ImplScaleColor();
893 				sal_uInt8 nGreen = ImplScaleColor();
894 				sal_uInt8 nBlue = ImplScaleColor();
895 				mpAcc->Erase( Color( nRed, nGreen, nBlue ) );
896 			}
897 		}
898 		break;
899 	}
900 }
901 
902 // ------------------------------------------------------------------------
903 
904 // for color type 0 and 4 (greyscale) the return value is always index to the color
905 //                2 and 6 (RGB)       the return value is always the 8 bit color component
906 sal_uInt8 PNGReaderImpl::ImplScaleColor()
907 {
908     sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 );
909 	sal_uInt16 nCol = ( *maDataIter++ << 8 );
910 
911 	nCol += *maDataIter++ & (sal_uInt16)nMask;
912 
913 	if ( mnPngDepth > 8 )	// convert 16bit graphics to 8
914 		nCol >>= 8;
915 
916 	return (sal_uInt8) nCol;
917 }
918 
919 // ------------------------------------------------------------------------
920 // ImplReadIDAT reads as much image data as needed
921 
922 void PNGReaderImpl::ImplReadIDAT()
923 {
924 	if( mnChunkLen > 0 )
925 	{
926 		if ( mbzCodecInUse == sal_False )
927 		{
928 			mbzCodecInUse = sal_True;
929 			mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT );
930 		}
931 		mpZCodec->SetBreak( mnChunkLen );
932 		SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, STREAM_READ );
933 
934 		while ( ( mpZCodec->GetBreak() ) )
935 		{
936             // get bytes needed to fill the current scanline
937             sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf);
938             sal_Int32 nRead = mpZCodec->ReadAsynchron( aIStrm, mpScanCurrent, nToRead );
939             if ( nRead < 0 )
940 			{
941 				mbStatus = sal_False;
942 				break;
943 			}
944 			if ( nRead < nToRead )
945 			{
946 				mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk
947 				break;
948 			}
949 			else  // this scanline is Finished
950 			{
951 				mpScanCurrent = mpInflateInBuf;
952                 ImplApplyFilter();
953 
954                 ImplDrawScanline( mnXStart, mnXAdd );
955                 mnYpos += mnYAdd;
956 			}
957 
958 			if ( mnYpos >= (sal_uInt32)maOrigSize.Height() )
959 			{
960                 if( (mnPass < 7) && mnInterlaceType )
961                     if( ImplPreparePass() )
962                         continue;
963                 mbIDAT = true;
964                 break;
965 			}
966 		}
967 	}
968 
969 	if( mbIDAT )
970 	{
971 		mpZCodec->EndCompression();
972 		mbzCodecInUse = sal_False;
973 	}
974 }
975 
976 // ---------------------------------------------------------------------------------------------------
977 
978 bool PNGReaderImpl::ImplPreparePass()
979 {
980     struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; };
981     static const InterlaceParams aInterlaceParams[8] =
982     {
983         // non-interlaced
984         { 0, 0, 1, 1 },
985         // Adam7-interlaced
986         { 0, 0, 8, 8 },    // pass 1
987         { 4, 0, 8, 8 },    // pass 2
988         { 0, 4, 4, 8 },    // pass 3
989         { 2, 0, 4, 4 },    // pass 4
990         { 0, 2, 2, 4 },    // pass 5
991         { 1, 0, 2, 2 },    // pass 6
992         { 0, 1, 1, 2 }     // pass 7
993     };
994 
995     const InterlaceParams* pParam = &aInterlaceParams[ 0 ];
996     if( mnInterlaceType )
997     {
998         while( ++mnPass <= 7 )
999         {
1000             pParam = &aInterlaceParams[ mnPass ];
1001 
1002             // skip this pass if the original image is too small for it
1003             if( (pParam->mnXStart < maOrigSize.Width())
1004             &&  (pParam->mnYStart < maOrigSize.Height()) )
1005                 break;
1006         }
1007         if( mnPass > 7 )
1008             return false;
1009 
1010         // skip the last passes if possible (for scaled down target images)
1011         if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) )
1012             return false;
1013     }
1014 
1015     mnYpos      = pParam->mnYStart;
1016     mnXStart    = pParam->mnXStart;
1017     mnXAdd      = pParam->mnXAdd;
1018     mnYAdd      = pParam->mnYAdd;
1019 
1020     // in Interlace mode the size of scanline is not constant
1021     // so first we calculate the number of entrys
1022     long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd;
1023     mnScansize = nScanWidth;
1024 
1025     if( mbRGBTriple )
1026         mnScansize = 3 * nScanWidth;
1027 
1028     if( mbAlphaChannel )
1029         mnScansize += nScanWidth;
1030 
1031     // convert to width in bytes
1032     mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3;
1033 
1034     ++mnScansize; // scan size also needs room for the filtertype byte
1035     rtl_zeroMemory( mpScanPrior, mnScansize );
1036 
1037     return true;
1038 }
1039 
1040 // ----------------------------------------------------------------------------
1041 // ImplApplyFilter writes the complete Scanline (nY)
1042 // in interlace mode the parameter nXStart and nXAdd are non-zero
1043 
1044 void PNGReaderImpl::ImplApplyFilter()
1045 {
1046 	OSL_ASSERT( mnScansize >= mnBPP + 1 );
1047     const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize;
1048 
1049     sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline
1050     switch ( nFilterType )
1051     {
1052         default: // unknown Scanline Filter Type
1053         case 0: // Filter Type "None"
1054             // we let the pixels pass and display the data unfiltered
1055             break;
1056 
1057         case 1: // Scanline Filter Type "Sub"
1058         {
1059             sal_uInt8* p1 = mpInflateInBuf + 1;
1060             const sal_uInt8* p2 = p1;
1061             p1 += mnBPP;
1062 
1063             // use left pixels
1064             do
1065                 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1066             while( ++p1 < pScanEnd );
1067         }
1068         break;
1069 
1070         case 2: // Scanline Filter Type "Up"
1071         {
1072             sal_uInt8* p1 = mpInflateInBuf + 1;
1073             const sal_uInt8* p2 = mpScanPrior + 1;
1074 
1075             // use pixels from prior line
1076 			while( p1 < pScanEnd )
1077 			{
1078                 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1079 				++p1;
1080 			}
1081         }
1082         break;
1083 
1084         case 3: // Scanline Filter Type "Average"
1085         {
1086             sal_uInt8* p1 = mpInflateInBuf + 1;
1087             const sal_uInt8* p2 = mpScanPrior + 1;
1088             const sal_uInt8* p3 = p1;
1089 
1090             // use one pixel from prior line
1091             for( int n = mnBPP; --n >= 0; ++p1, ++p2)
1092                 *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) );
1093 
1094             // predict by averaging the left and prior line pixels
1095 			while( p1 < pScanEnd )
1096 			{
1097                 *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) );
1098 				++p1;
1099 			}
1100         }
1101         break;
1102 
1103         case 4: // Scanline Filter Type "PaethPredictor"
1104         {
1105             sal_uInt8* p1 = mpInflateInBuf + 1;
1106             const sal_uInt8* p2 = mpScanPrior + 1;
1107             const sal_uInt8* p3 = p1;
1108             const sal_uInt8* p4 = p2;
1109 
1110             // use one pixel from prior line
1111             for( int n = mnBPP; --n >= 0; ++p1)
1112                 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1113 
1114             // predict by using the left and the prior line pixels
1115             while( p1 < pScanEnd )
1116             {
1117                 int na = *(p2++);
1118                 int nb = *(p3++);
1119                 int nc = *(p4++);
1120 
1121                 int npa = nb - (int)nc;
1122                 int npb = na - (int)nc;
1123                 int npc = npa + npb;
1124 
1125                 if( npa < 0 )
1126                     npa =-npa;
1127                 if( npb < 0 )
1128                     npb =-npb;
1129                 if( npc < 0 )
1130                     npc =-npc;
1131 
1132                 if( npa > npb )
1133                     na = nb, npa = npb;
1134                 if( npa > npc )
1135                     na = nc;
1136 
1137                 *p1 = static_cast<sal_uInt8>( *p1 + na );
1138 				++p1;
1139 			}
1140         }
1141         break;
1142     }
1143 
1144     rtl_copyMemory( mpScanPrior, mpInflateInBuf, mnScansize );
1145 }
1146 
1147 // ---------------------------------------------------------------------------------------------------
1148 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1149 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1150 
1151 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd )
1152 {
1153     // optimization for downscaling
1154     if( mnYpos & mnPreviewMask )
1155         return;
1156     if( nXStart & mnPreviewMask )
1157         return;
1158 
1159     // convert nY to pixel units in the target image
1160     // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1161     const sal_uInt32 nY = mnYpos >> mnPreviewShift;
1162 
1163     const sal_uInt8* pTmp = mpInflateInBuf + 1;
1164 	if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1165 	{
1166 		switch ( mpAcc->GetBitCount() )
1167 		{
1168 			case 1 :
1169 			{
1170 				if ( mbTransparent )
1171 				{
1172                     for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1173 					{
1174                         sal_uInt8 nCol;
1175                         nShift = (nShift - 1) & 7;
1176 						if ( nShift == 0 )
1177 							nCol = *(pTmp++);
1178 						else
1179 							nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1180                         nCol &= 1;
1181 
1182 						ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1183 					}
1184 				}
1185 				else
1186 				{   // BMP_FORMAT_1BIT_MSB_PAL
1187 					for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1188 					{
1189                         nShift = (nShift - 1) & 7;
1190 
1191                         sal_uInt8 nCol;
1192 						if ( nShift == 0 )
1193 							nCol = *(pTmp++);
1194 						else
1195 							nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1196                         nCol &= 1;
1197 
1198                         ImplSetPixel( nY, nX, nCol );
1199 					}
1200 				}
1201 			}
1202 			break;
1203 
1204 			case 4 :
1205 			{
1206 				if ( mbTransparent )
1207 				{
1208 					if ( mnPngDepth == 4 )	// check if source has a two bit pixel format
1209 					{
1210 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex )
1211 						{
1212 							if( nXIndex & 1 )
1213 							{
1214 								ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] );
1215 								pTmp++;
1216 							}
1217 							else
1218 							{
1219 								ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] );
1220 							}
1221 						}
1222 					}
1223 					else // if ( mnPngDepth == 2 )
1224 					{
1225 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1226 						{
1227                             sal_uInt8 nCol;
1228 							switch( nXIndex & 3 )
1229 							{
1230 								case 0 :
1231 									nCol = *pTmp >> 6;
1232 								break;
1233 
1234 								case 1 :
1235 									nCol = ( *pTmp >> 4 ) & 0x03 ;
1236 								break;
1237 
1238 								case 2 :
1239 									nCol = ( *pTmp >> 2 ) & 0x03;
1240 								break;
1241 
1242 								case 3 :
1243 									nCol = ( *pTmp++ ) & 0x03;
1244 								break;
1245 
1246                                 default:    // get rid of nCol uninitialized warning
1247                                     nCol = 0;
1248                                     break;
1249 							}
1250 
1251 							ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1252 						}
1253 					}
1254 				}
1255 				else
1256 				{
1257 					if ( mnPngDepth == 4 )	// maybe the source is a two bitmap graphic
1258 					{   // BMP_FORMAT_4BIT_LSN_PAL
1259 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1260 						{
1261 							if( nXIndex & 1 )
1262 								ImplSetPixel( nY, nX, *pTmp++ & 0x0f );
1263 							else
1264 								ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f );
1265 						}
1266 					}
1267 					else // if ( mnPngDepth == 2 )
1268 					{
1269 						for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1270 						{
1271 							switch( nXIndex & 3 )
1272 							{
1273 								case 0 :
1274 									ImplSetPixel( nY, nX, *pTmp >> 6 );
1275 								break;
1276 
1277 								case 1 :
1278 									ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 );
1279 								break;
1280 
1281 								case 2 :
1282 									ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 );
1283 								break;
1284 
1285 								case 3 :
1286 									ImplSetPixel( nY, nX, *pTmp++ & 0x03 );
1287 								break;
1288 							}
1289 						}
1290 					}
1291 				}
1292 			}
1293 			break;
1294 
1295 			case 8 :
1296 			{
1297 				if ( mbAlphaChannel )
1298 				{
1299 					if ( mnPngDepth == 8 )	// maybe the source is a 16 bit grayscale
1300 					{
1301 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1302 							ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] );
1303 					}
1304 					else
1305 					{
1306 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1307 							ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] );
1308 					}
1309 				}
1310 				else if ( mbTransparent )
1311 				{
1312 					if ( mnPngDepth == 8 )	// maybe the source is a 16 bit grayscale
1313 					{
1314 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ )
1315 							ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1316 					}
1317 					else
1318 					{
1319 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1320 							ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1321 					}
1322 				}
1323 				else // neither alpha nor transparency
1324 				{
1325 					if ( mnPngDepth == 8 )	// maybe the source is a 16 bit grayscale
1326 					{
1327                         if( nXAdd == 1 && mnPreviewShift == 0 )  // copy raw line data if possible
1328                         {
1329                             int nLineBytes = maOrigSize.Width();
1330                             mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes );
1331                             pTmp += nLineBytes;
1332                         }
1333                         else
1334                         {
1335                             for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd )
1336                                 ImplSetPixel( nY, nX, *pTmp++ );
1337                         }
1338 					}
1339 					else
1340 					{
1341 						for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1342 							ImplSetPixel( nY, nX, *pTmp );
1343 					}
1344 				}
1345 			}
1346 			break;
1347 
1348 			default :
1349 				mbStatus = sal_False;
1350 			break;
1351 		}
1352 	}
1353 	else // no palette => truecolor
1354 	{
1355 		if( mbAlphaChannel ) // has RGB + alpha
1356 		{   // BMP_FORMAT_32BIT_TC_RGBA
1357 			if ( mnPngDepth == 8 )  // maybe the source has 16 bit per sample
1358 			{
1359                 if ( mpColorTable != mpDefaultColorTable )
1360                 {
1361                     for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1362                        ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ],
1363                                                                mpColorTable[ pTmp[ 1 ] ],
1364                                                                mpColorTable[ pTmp[ 2 ] ] ), pTmp[ 3 ] );
1365                 }
1366                 else
1367                 {
1368 //                  if ( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible
1369 //                  {
1370 //                      int nLineBytes = 4 * maOrigSize.Width();
1371 //                      mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_32BIT_TC_RGBA, nLineBytes );
1372 //                      pTmp += nLineBytes;
1373 //                  }
1374 //                  else
1375                     {
1376                         for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1377                             ImplSetAlphaPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ), pTmp[3] );
1378                     }
1379                 }
1380 			}
1381 			else
1382 			{   // BMP_FORMAT_64BIT_TC_RGBA
1383 				for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 )
1384 					ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ],
1385 														mpColorTable[ pTmp[ 2 ] ],
1386 														mpColorTable[ pTmp[ 4 ] ] ), pTmp[6] );
1387 			}
1388 		}
1389 		else if( mbTransparent ) // has RGB + transparency
1390 		{   // BMP_FORMAT_24BIT_TC_RGB
1391 			if ( mnPngDepth == 8 )  // maybe the source has 16 bit per sample
1392 			{
1393 				for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1394 				{
1395 					sal_uInt8 nRed = pTmp[ 0 ];
1396 					sal_uInt8 nGreen = pTmp[ 1 ];
1397 					sal_uInt8 nBlue = pTmp[ 2 ];
1398 					sal_Bool bTransparent = ( ( nRed == mnTransRed )
1399 							 			&& ( nGreen == mnTransGreen )
1400 										&& ( nBlue == mnTransBlue ) );
1401 
1402 					ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1403 														mpColorTable[ nGreen ],
1404 														mpColorTable[ nBlue ] ), bTransparent );
1405 				}
1406 			}
1407 			else
1408 			{   // BMP_FORMAT_48BIT_TC_RGB
1409 				for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1410 				{
1411 					sal_uInt8 nRed = pTmp[ 0 ];
1412 					sal_uInt8 nGreen = pTmp[ 2 ];
1413 					sal_uInt8 nBlue = pTmp[ 4 ];
1414 					sal_Bool bTransparent = ( ( nRed == mnTransRed )
1415 										&& ( nGreen == mnTransGreen )
1416 										&& ( nBlue == mnTransBlue ) );
1417 
1418 					ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1419 														mpColorTable[ nGreen ],
1420 														mpColorTable[ nBlue ] ), bTransparent );
1421 				}
1422 			}
1423 		}
1424 		else  // has RGB but neither alpha nor transparency
1425 		{   // BMP_FORMAT_24BIT_TC_RGB
1426 			if ( mnPngDepth == 8 )   // maybe the source has 16 bit per sample
1427 			{
1428                 if ( mpColorTable != mpDefaultColorTable )
1429                 {
1430                     for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1431                         ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ],
1432                                                             mpColorTable[ pTmp[ 1 ] ],
1433                                                             mpColorTable[ pTmp[ 2 ] ] ) );
1434                 }
1435                 else
1436                 {
1437                     if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible
1438                     {
1439                         int nLineBytes = maOrigSize.Width() * 3;
1440                         mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_24BIT_TC_RGB, nLineBytes );
1441                         pTmp += nLineBytes;
1442                     }
1443                     else
1444                     {
1445                         for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1446                             ImplSetPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ) );
1447                     }
1448                 }
1449 			}
1450 			else
1451 			{   // BMP_FORMAT_48BIT_TC_RGB
1452 				for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1453 					ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ],
1454 														mpColorTable[ pTmp[ 2 ] ],
1455 														mpColorTable[ pTmp[ 4 ] ] ) );
1456 			}
1457 		}
1458 	}
1459 }
1460 
1461 // ------------------------------------------------------------------------
1462 
1463 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor )
1464 {
1465     // TODO: get preview mode checks out of inner loop
1466     if( nX & mnPreviewMask )
1467         return;
1468     nX >>= mnPreviewShift;
1469 
1470     mpAcc->SetPixel( nY, nX, rBitmapColor );
1471 }
1472 
1473 // ------------------------------------------------------------------------
1474 
1475 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex )
1476 {
1477     // TODO: get preview mode checks out of inner loop
1478     if( nX & mnPreviewMask )
1479         return;
1480     nX >>= mnPreviewShift;
1481 
1482     mpAcc->SetPixel( nY, nX, nPalIndex );
1483 }
1484 
1485 // ------------------------------------------------------------------------
1486 
1487 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, sal_Bool bTrans )
1488 {
1489     // TODO: get preview mode checks out of inner loop
1490     if( nX & mnPreviewMask )
1491         return;
1492     nX >>= mnPreviewShift;
1493 
1494     mpAcc->SetPixel( nY, nX, rBitmapColor );
1495 
1496     if ( bTrans )
1497         mpMaskAcc->SetPixel( nY, nX, mcTranspColor );
1498     else
1499         mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor );
1500 }
1501 
1502 // ------------------------------------------------------------------------
1503 
1504 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1505     sal_uInt8 nPalIndex, sal_uInt8 nAlpha )
1506 {
1507     // TODO: get preview mode checks out of inner loop
1508     if( nX & mnPreviewMask )
1509         return;
1510     nX >>= mnPreviewShift;
1511 
1512     mpAcc->SetPixel( nY, nX, nPalIndex );
1513     mpMaskAcc->SetPixel( nY, nX, ~nAlpha );
1514 }
1515 
1516 // ------------------------------------------------------------------------
1517 
1518 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1519     const BitmapColor& rBitmapColor, sal_uInt8 nAlpha )
1520 {
1521     // TODO: get preview mode checks out of inner loop
1522     if( nX & mnPreviewMask )
1523         return;
1524     nX >>= mnPreviewShift;
1525 
1526     mpAcc->SetPixel( nY, nX, rBitmapColor );
1527     mpMaskAcc->SetPixel( nY, nX, ~nAlpha );
1528 }
1529 
1530 // ------------------------------------------------------------------------
1531 
1532 sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32()
1533 {
1534 	sal_uInt32 nRet;
1535 	nRet = *maDataIter++;
1536 	nRet <<= 8;
1537 	nRet |= *maDataIter++;
1538 	nRet <<= 8;
1539 	nRet |= *maDataIter++;
1540 	nRet <<= 8;
1541 	nRet |= *maDataIter++;
1542 	return nRet;
1543 }
1544 
1545 // ------------------------------------------------------------------------
1546 
1547 // -------------
1548 // - PNGReader -
1549 // -------------
1550 
1551 PNGReader::PNGReader( SvStream& rIStm ) :
1552 	mpImpl( new ::vcl::PNGReaderImpl( rIStm ) )
1553 {
1554 }
1555 
1556 // ------------------------------------------------------------------------
1557 
1558 PNGReader::~PNGReader()
1559 {
1560 	delete mpImpl;
1561 }
1562 
1563 // ------------------------------------------------------------------------
1564 
1565 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint )
1566 {
1567     return mpImpl->GetBitmapEx( i_rPreviewSizeHint );
1568 }
1569 
1570 // ------------------------------------------------------------------------
1571 
1572 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const
1573 {
1574 	return mpImpl->GetAllChunks();
1575 }
1576 
1577 // ------------------------------------------------------------------------
1578 
1579 void PNGReader::SetIgnoreGammaChunk( sal_Bool b )
1580 {
1581 	mpImpl->SetIgnoreGammaChunk( b );
1582 }
1583 
1584 
1585 } // namespace vcl
1586