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