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