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