update emoji autocorrect entries from po-files
[LibreOffice.git] / vcl / source / gdi / pngread.cxx
blobd9352a79202c56c9290fd39eed683d00b79da633
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/pngread.hxx>
22 #include <cmath>
23 #include <rtl/crc.h>
24 #include <rtl/alloc.h>
25 #include <tools/zcodec.hxx>
26 #include <tools/stream.hxx>
27 #include <vcl/bmpacc.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/alpha.hxx>
30 #include <osl/endian.h>
32 namespace vcl
35 #define PNGCHUNK_IHDR 0x49484452
36 #define PNGCHUNK_PLTE 0x504c5445
37 #define PNGCHUNK_IDAT 0x49444154
38 #define PNGCHUNK_IEND 0x49454e44
39 #define PNGCHUNK_bKGD 0x624b4744
40 #define PNGCHUNK_gAMA 0x67414d41
41 #define PNGCHUNK_pHYs 0x70485973
42 #define PNGCHUNK_tRNS 0x74524e53
44 #define VIEWING_GAMMA 2.35
45 #define DISPLAY_GAMMA 1.0
48 static const sal_uInt8 mpDefaultColorTable[ 256 ] =
49 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
50 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
51 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
52 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
54 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
55 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
56 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
58 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
59 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
60 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
61 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
62 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
63 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
64 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
67 class PNGReaderImpl
69 private:
70 SvStream& mrPNGStream;
71 SvStreamEndian mnOrigStreamMode;
73 std::vector<vcl::PNGReader::ChunkData> maChunkSeq;
74 std::vector<vcl::PNGReader::ChunkData>::iterator maChunkIter;
75 std::vector<sal_uInt8>::iterator maDataIter;
77 Bitmap* mpBmp;
78 BitmapWriteAccess* mpAcc;
79 Bitmap* mpMaskBmp;
80 AlphaMask* mpAlphaMask;
81 BitmapWriteAccess* mpMaskAcc;
82 ZCodec mpZCodec;
83 sal_uInt8* mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1
84 sal_uInt8* mpScanPrior; // pointer to the latest scanline
85 sal_uInt8* mpTransTab; // for transparency in images with palette colortype
86 sal_uInt8* mpScanCurrent; // pointer into the current scanline
87 sal_uInt8* mpColorTable;
88 sal_Size mnStreamSize; // estimate of PNG file size
89 sal_uInt32 mnChunkType; // Type of current PNG chunk
90 sal_Int32 mnChunkLen; // Length of current PNG chunk
91 Size maOrigSize; // pixel size of the full image
92 Size maTargetSize; // pixel size of the result image
93 Size maPhysSize; // preferred size in MAP_100TH_MM units
94 sal_uInt32 mnBPP; // number of bytes per pixel
95 sal_uInt32 mnScansize; // max size of scanline
96 sal_uInt32 mnYpos; // latest y position in full image
97 int mnPass; // if interlaced the latest pass ( 1..7 ) else 7
98 sal_uInt32 mnXStart; // the starting X for the current pass
99 sal_uInt32 mnXAdd; // the increment for input images X coords for the current pass
100 sal_uInt32 mnYAdd; // the increment for input images Y coords for the current pass
101 int mnPreviewShift; // shift to convert orig image coords into preview image coords
102 int mnPreviewMask; // == ((1 << mnPreviewShift) - 1)
103 sal_uInt16 mnTargetDepth; // pixel depth of target bitmap
104 sal_uInt8 mnTransRed;
105 sal_uInt8 mnTransGreen;
106 sal_uInt8 mnTransBlue;
107 sal_uInt8 mnPngDepth; // pixel depth of PNG data
108 sal_uInt8 mnColorType;
109 sal_uInt8 mnCompressionType;
110 sal_uInt8 mnFilterType;
111 sal_uInt8 mnInterlaceType;
112 BitmapColor mcTranspColor; // transparency mask's transparency "color"
113 BitmapColor mcOpaqueColor; // transparency mask's opaque "color"
114 bool mbTransparent : 1; // graphic includes an tRNS Chunk or an alpha Channel
115 bool mbAlphaChannel : 1; // is true for ColorType 4 and 6
116 bool mbRGBTriple : 1;
117 bool mbPalette : 1; // false if we need a Palette
118 bool mbGrayScale : 1;
119 bool mbzCodecInUse : 1;
120 bool mbStatus : 1;
121 bool mbIDAT : 1; // true if finished with enough IDAT chunks
122 bool mbGamma : 1; // true if Gamma Correction available
123 bool mbpHYs : 1; // true if pysical size of pixel available
124 bool mbIgnoreGammaChunk : 1;
126 #if OSL_DEBUG_LEVEL > 0
127 // do some checks in debug mode
128 sal_Int32 mnAllocSizeScanline;
129 sal_Int32 mnAllocSizeScanlineAlpha;
130 #endif
131 // the temporary Scanline (and alpha) for direct scanline copy to Bitmap
132 sal_uInt8* mpScanline;
133 sal_uInt8* mpScanlineAlpha;
135 bool ReadNextChunk();
136 void ReadRemainingChunks();
138 void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & );
139 void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex );
140 void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, bool bTrans );
141 void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha );
142 void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha );
143 void ImplReadIDAT();
144 bool ImplPreparePass();
145 void ImplApplyFilter();
146 void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd );
147 bool ImplReadTransparent();
148 void ImplGetGamma();
149 void ImplGetBackground();
150 sal_uInt8 ImplScaleColor();
151 bool ImplReadHeader( const Size& rPreviewSizeHint );
152 bool ImplReadPalette();
153 void ImplGetGrayPalette( sal_uInt16 );
154 sal_uInt32 ImplReadsal_uInt32();
156 public:
158 explicit PNGReaderImpl( SvStream& );
159 ~PNGReaderImpl();
161 BitmapEx GetBitmapEx( const Size& rPreviewSizeHint );
162 const std::vector<vcl::PNGReader::ChunkData>& GetAllChunks();
163 void SetIgnoreGammaChunk( bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; };
166 PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream )
167 : mrPNGStream( rPNGStream ),
168 mpBmp ( NULL ),
169 mpAcc ( NULL ),
170 mpMaskBmp ( NULL ),
171 mpAlphaMask ( NULL ),
172 mpMaskAcc ( NULL ),
173 mpInflateInBuf ( NULL ),
174 mpScanPrior ( NULL ),
175 mpTransTab ( NULL ),
176 mpScanCurrent ( NULL ),
177 mpColorTable ( const_cast<sal_uInt8*>(mpDefaultColorTable) ),
178 mnChunkType ( 0 ),
179 mnChunkLen ( 0 ),
180 mnBPP ( 0 ),
181 mnScansize ( 0 ),
182 mnYpos ( 0 ),
183 mnPass ( 0 ),
184 mnXStart ( 0 ),
185 mnXAdd ( 0 ),
186 mnYAdd ( 0 ),
187 mnTargetDepth ( 0 ),
188 mnTransRed ( 0 ),
189 mnTransGreen ( 0 ),
190 mnTransBlue ( 0 ),
191 mnPngDepth ( 0 ),
192 mnColorType ( 0 ),
193 mnCompressionType( 0 ),
194 mnFilterType ( 0 ),
195 mnInterlaceType ( 0 ),
196 mbTransparent( false ),
197 mbAlphaChannel( false ),
198 mbRGBTriple( false ),
199 mbPalette( false ),
200 mbGrayScale( false ),
201 mbzCodecInUse ( false ),
202 mbStatus( true ),
203 mbIDAT( false ),
204 mbGamma ( false ),
205 mbpHYs ( false ),
206 mbIgnoreGammaChunk ( false ),
207 #if OSL_DEBUG_LEVEL > 0
208 mnAllocSizeScanline(0),
209 mnAllocSizeScanlineAlpha(0),
210 #endif
211 mpScanline(0),
212 mpScanlineAlpha(0)
214 // prepare the PNG data stream
215 mnOrigStreamMode = mrPNGStream.GetEndian();
216 mrPNGStream.SetEndian( SvStreamEndian::BIG );
218 // prepare the chunk reader
219 maChunkSeq.reserve( 16 );
220 maChunkIter = maChunkSeq.begin();
222 // estimate PNG file size (to allow sanity checks)
223 const sal_Size nStreamPos = mrPNGStream.Tell();
224 mrPNGStream.Seek( STREAM_SEEK_TO_END );
225 mnStreamSize = mrPNGStream.Tell();
226 mrPNGStream.Seek( nStreamPos );
228 // check the PNG header magic
229 sal_uInt32 nDummy = 0;
230 mrPNGStream.ReadUInt32( nDummy );
231 mbStatus = (nDummy == 0x89504e47);
232 mrPNGStream.ReadUInt32( nDummy );
233 mbStatus = (nDummy == 0x0d0a1a0a) && mbStatus;
235 mnPreviewShift = 0;
236 mnPreviewMask = (1 << mnPreviewShift) - 1;
239 PNGReaderImpl::~PNGReaderImpl()
241 mrPNGStream.SetEndian( mnOrigStreamMode );
243 if ( mbzCodecInUse )
244 mpZCodec.EndCompression();
246 if( mpColorTable != mpDefaultColorTable )
247 delete[] mpColorTable;
249 delete mpBmp;
250 delete mpAlphaMask;
251 delete mpMaskBmp;
252 delete[] mpTransTab;
253 delete[] mpInflateInBuf;
254 delete[] mpScanPrior;
256 delete[] mpScanline;
257 delete[] mpScanlineAlpha;
260 bool PNGReaderImpl::ReadNextChunk()
262 if( maChunkIter == maChunkSeq.end() )
264 // get the next chunk from the stream
266 // unless we are at the end of the PNG stream
267 if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) )
268 return false;
269 if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
270 return false;
272 PNGReader::ChunkData aDummyChunk;
273 maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk );
274 PNGReader::ChunkData& rChunkData = *maChunkIter;
276 // read the chunk header
277 mrPNGStream.ReadInt32( mnChunkLen ).ReadUInt32( mnChunkType );
278 rChunkData.nType = mnChunkType;
280 // fdo#61847 truncate over-long, trailing chunks
281 const sal_Size nStreamPos = mrPNGStream.Tell();
282 if( mnChunkLen < 0 || nStreamPos + mnChunkLen >= mnStreamSize )
283 mnChunkLen = mnStreamSize - nStreamPos;
285 // calculate chunktype CRC (swap it back to original byte order)
286 sal_uInt32 nChunkType = mnChunkType;
287 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
288 nChunkType = OSL_SWAPDWORD( nChunkType );
289 #endif
290 sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 );
292 // read the chunk data and check the CRC
293 if( mnChunkLen && !mrPNGStream.IsEof() )
295 rChunkData.aData.resize( mnChunkLen );
297 sal_Int32 nBytesRead = 0;
298 do {
299 sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ];
300 nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead );
301 } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) );
303 nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen );
304 maDataIter = rChunkData.aData.begin();
306 sal_uInt32 nCheck(0);
307 mrPNGStream.ReadUInt32( nCheck );
308 if( nCRC32 != nCheck )
309 return false;
311 else
313 // the next chunk was already read
314 mnChunkType = (*maChunkIter).nType;
315 mnChunkLen = (*maChunkIter).aData.size();
316 maDataIter = (*maChunkIter).aData.begin();
319 ++maChunkIter;
320 if( mnChunkType == PNGCHUNK_IEND )
321 return false;
322 return true;
325 // read the remaining chunks from mrPNGStream
326 void PNGReaderImpl::ReadRemainingChunks()
328 while( ReadNextChunk() ) ;
331 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks()
333 ReadRemainingChunks();
334 return maChunkSeq;
337 BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint )
339 // reset to the first chunk
340 maChunkIter = maChunkSeq.begin();
342 // first chunk must be IDHR
343 if( mbStatus && ReadNextChunk() )
345 if (mnChunkType == PNGCHUNK_IHDR)
346 mbStatus = ImplReadHeader( rPreviewSizeHint );
347 else
348 mbStatus = false;
351 // parse the remaining chunks
352 while (mbStatus && !mbIDAT && ReadNextChunk())
354 switch( mnChunkType )
356 case PNGCHUNK_IHDR :
358 mbStatus = false; //IHDR should only appear as the first chunk
360 break;
362 case PNGCHUNK_gAMA : // the gamma chunk must precede
363 { // the 'IDAT' and also the 'PLTE'(if available )
364 if ( !mbIgnoreGammaChunk && !mbIDAT )
365 ImplGetGamma();
367 break;
369 case PNGCHUNK_PLTE :
371 if ( !mbPalette )
372 mbStatus = ImplReadPalette();
374 break;
376 case PNGCHUNK_tRNS :
378 if ( !mbIDAT ) // the tRNS chunk must precede the IDAT
379 mbStatus = ImplReadTransparent();
381 break;
383 case PNGCHUNK_bKGD : // the background chunk must appear
385 if ( !mbIDAT && mbPalette ) // before the 'IDAT' and after the
386 ImplGetBackground(); // PLTE(if available ) chunk.
388 break;
390 case PNGCHUNK_IDAT :
392 if ( !mpInflateInBuf ) // taking care that the header has properly been read
393 mbStatus = false;
394 else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes
395 ImplReadIDAT();
397 break;
399 case PNGCHUNK_pHYs :
401 if ( !mbIDAT && mnChunkLen == 9 )
403 sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32();
404 sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32();
406 sal_uInt8 nUnitSpecifier = *maDataIter++;
407 if( (nUnitSpecifier == 1) && nXPixelPerMeter && nYPixelPerMeter )
409 mbpHYs = true;
411 // convert into MAP_100TH_MM
412 maPhysSize.Width() = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter );
413 maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter );
417 break;
419 case PNGCHUNK_IEND:
420 mbStatus = mbIDAT; // there is a problem if the image is not complete yet
421 break;
425 // release write access of the bitmaps
426 if ( mpAcc )
427 Bitmap::ReleaseAccess( mpAcc ), mpAcc = NULL;
429 if ( mpMaskAcc )
431 if ( mpAlphaMask )
432 mpAlphaMask->ReleaseAccess( mpMaskAcc );
433 else if ( mpMaskBmp )
434 Bitmap::ReleaseAccess( mpMaskAcc );
436 mpMaskAcc = NULL;
439 // return the resulting BitmapEx
440 BitmapEx aRet;
442 if( !mbStatus || !mbIDAT )
443 aRet.Clear();
444 else
446 if ( mpAlphaMask )
447 aRet = BitmapEx( *mpBmp, *mpAlphaMask );
448 else if ( mpMaskBmp )
449 aRet = BitmapEx( *mpBmp, *mpMaskBmp );
450 else
451 aRet = *mpBmp;
453 if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() )
455 aRet.SetPrefMapMode( MAP_100TH_MM );
456 aRet.SetPrefSize( maPhysSize );
459 return aRet;
462 bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint )
464 if( mnChunkLen < 13 )
465 return false;
467 maOrigSize.Width() = ImplReadsal_uInt32();
468 maOrigSize.Height() = ImplReadsal_uInt32();
470 if (maOrigSize.Width() <= 0 || maOrigSize.Height() <= 0)
471 return false;
473 mnPngDepth = *(maDataIter++);
474 mnColorType = *(maDataIter++);
476 mnCompressionType = *(maDataIter++);
477 if( mnCompressionType != 0 ) // unknown compression type
478 return false;
480 mnFilterType = *(maDataIter++);
481 if( mnFilterType != 0 ) // unknown filter type
482 return false;
484 mnInterlaceType = *(maDataIter++);
485 switch ( mnInterlaceType ) // filter type valid ?
487 case 0 : // progressive image
488 mnPass = 7;
489 break;
490 case 1 : // Adam7-interlaced image
491 mnPass = 0;
492 break;
493 default:
494 return false;
497 mbPalette = true;
498 mbIDAT = mbAlphaChannel = mbTransparent = false;
499 mbGrayScale = mbRGBTriple = false;
500 mnTargetDepth = mnPngDepth;
501 sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3;
503 // valid color types are 0,2,3,4 & 6
504 switch ( mnColorType )
506 case 0 : // each pixel is a grayscale
508 switch ( mnPngDepth )
510 case 2 : // 2bit target not available -> use four bits
511 mnTargetDepth = 4; // we have to expand the bitmap
512 mbGrayScale = true;
513 break;
514 case 16 :
515 mnTargetDepth = 8; // we have to reduce the bitmap
516 // fall through
517 case 1 :
518 case 4 :
519 case 8 :
520 mbGrayScale = true;
521 break;
522 default :
523 return false;
526 break;
528 case 2 : // each pixel is an RGB triple
530 mbRGBTriple = true;
531 nScansize64 *= 3;
532 switch ( mnPngDepth )
534 case 16 : // we have to reduce the bitmap
535 case 8 :
536 mnTargetDepth = 24;
537 break;
538 default :
539 return false;
542 break;
544 case 3 : // each pixel is a palette index
546 switch ( mnPngDepth )
548 case 2 :
549 mnTargetDepth = 4; // we have to expand the bitmap
550 // fall through
551 case 1 :
552 case 4 :
553 case 8 :
554 mbPalette = false;
555 break;
556 default :
557 return false;
560 break;
562 case 4 : // each pixel is a grayscale sample followed by an alpha sample
564 nScansize64 *= 2;
565 mbAlphaChannel = true;
566 switch ( mnPngDepth )
568 case 16 :
569 mnTargetDepth = 8; // we have to reduce the bitmap
570 case 8 :
571 mbGrayScale = true;
572 break;
573 default :
574 return false;
577 break;
579 case 6 : // each pixel is an RGB triple followed by an alpha sample
581 mbRGBTriple = true;
582 nScansize64 *= 4;
583 mbAlphaChannel = true;
584 switch (mnPngDepth )
586 case 16 : // we have to reduce the bitmap
587 case 8 :
588 mnTargetDepth = 24;
589 break;
590 default :
591 return false;
594 break;
596 default :
597 return false;
600 mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() );
601 if ( !mnBPP )
602 mnBPP = 1;
604 nScansize64++; // each scanline includes one filterbyte
606 if ( nScansize64 > SAL_MAX_UINT32 )
607 return false;
609 mnScansize = static_cast< sal_uInt32 >( nScansize64 );
611 // calculate target size from original size and the preview hint
612 if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() )
614 Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() );
615 maTargetSize = maOrigSize;
617 if( aPreviewSize.Width() == 0 ) {
618 aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() );
619 if( aPreviewSize.Width() <= 0 )
620 aPreviewSize.setWidth( 1 );
621 } else if( aPreviewSize.Height() == 0 ) {
622 aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() );
623 if( aPreviewSize.Height() <= 0 )
624 aPreviewSize.setHeight( 1 );
627 if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) {
628 OSL_TRACE("preview size %ldx%ld", aPreviewSize.Width(), aPreviewSize.Height() );
630 for( int i = 1; i < 5; ++i )
632 if( (maTargetSize.Width() >> i) < aPreviewSize.Width() )
633 break;
634 if( (maTargetSize.Height() >> i) < aPreviewSize.Height() )
635 break;
636 mnPreviewShift = i;
638 mnPreviewMask = (1 << mnPreviewShift) - 1;
642 maTargetSize.Width() = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift;
643 maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift;
645 //round bits up to nearest multiple of 8 and divide by 8 to get num of bytes per pixel
646 int nBytesPerPixel = ((mnTargetDepth + 7) & ~7)/8;
648 //stupidly big, forget about it
649 if (maTargetSize.Width() >= SAL_MAX_INT32 / nBytesPerPixel / maTargetSize.Height())
651 SAL_WARN( "vcl.gdi", "overlarge png dimensions: " <<
652 maTargetSize.Width() << " x " << maTargetSize.Height() << " depth: " << mnTargetDepth);
653 return false;
656 // TODO: switch between both scanlines instead of copying
657 mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ];
658 mpScanCurrent = mpInflateInBuf;
659 mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ];
661 if ( !mpInflateInBuf || !mpScanPrior )
662 return false;
664 mpBmp = new Bitmap( maTargetSize, mnTargetDepth );
665 mpAcc = mpBmp->AcquireWriteAccess();
666 if( !mpAcc )
667 return false;
669 if ( mbAlphaChannel )
671 mpAlphaMask = new AlphaMask( maTargetSize );
672 mpAlphaMask->Erase( 128 );
673 mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
674 if( !mpMaskAcc )
675 return false;
678 if ( mbGrayScale )
679 ImplGetGrayPalette( mnPngDepth );
681 ImplPreparePass();
683 return true;
686 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth )
688 if( nBitDepth > 8 )
689 nBitDepth = 8;
691 sal_uInt16 nPaletteEntryCount = 1 << nBitDepth;
692 sal_uInt32 nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0;
694 // no bitdepth==2 available
695 // but bitdepth==4 with two unused bits is close enough
696 if( nBitDepth == 2 )
697 nPaletteEntryCount = 16;
699 mpAcc->SetPaletteEntryCount( nPaletteEntryCount );
700 for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd )
701 mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ],
702 mpColorTable[ nStart ], mpColorTable[ nStart ] ) );
705 bool PNGReaderImpl::ImplReadPalette()
707 sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 );
709 if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc )
711 mbPalette = true;
712 mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount );
714 for ( sal_uInt16 i = 0; i < nCount; i++ )
716 sal_uInt8 nRed = mpColorTable[ *maDataIter++ ];
717 sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ];
718 sal_uInt8 nBlue = mpColorTable[ *maDataIter++ ];
719 mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) );
722 else
723 mbStatus = false;
725 return mbStatus;
728 bool PNGReaderImpl::ImplReadTransparent()
730 bool bNeedAlpha = false;
732 if ( mpTransTab == NULL )
734 switch ( mnColorType )
736 case 0 :
738 if ( mnChunkLen == 2 )
740 mpTransTab = new sal_uInt8[ 256 ];
741 memset( mpTransTab, 0xff, 256);
742 // color type 0 and 4 is always greyscale,
743 // so the return value can be used as index
744 sal_uInt8 nIndex = ImplScaleColor();
745 mpTransTab[ nIndex ] = 0;
746 mbTransparent = true;
749 break;
751 case 2 :
753 if ( mnChunkLen == 6 )
755 mnTransRed = ImplScaleColor();
756 mnTransGreen = ImplScaleColor();
757 mnTransBlue = ImplScaleColor();
758 mbTransparent = true;
761 break;
763 case 3 :
765 if ( mnChunkLen <= 256 )
767 mbTransparent = true;
768 mpTransTab = new sal_uInt8 [ 256 ];
769 memset( mpTransTab, 0xff, 256 );
770 if (mnChunkLen > 0)
772 memcpy( mpTransTab, &(*maDataIter), mnChunkLen );
773 maDataIter += mnChunkLen;
774 // need alpha transparency if not on/off masking
775 for( int i = 0; i < mnChunkLen; ++i )
776 bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF);
780 break;
784 if( mbTransparent && !mbAlphaChannel && !mpMaskBmp )
786 if( bNeedAlpha)
788 mpAlphaMask = new AlphaMask( maTargetSize );
789 mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
791 else
793 mpMaskBmp = new Bitmap( maTargetSize, 1 );
794 mpMaskAcc = mpMaskBmp->AcquireWriteAccess();
796 mbTransparent = (mpMaskAcc != NULL);
797 if( !mbTransparent )
798 return false;
799 mcOpaqueColor = BitmapColor( 0x00 );
800 mcTranspColor = BitmapColor( 0xFF );
801 mpMaskAcc->Erase( 0x00 );
804 return true;
807 void PNGReaderImpl::ImplGetGamma()
809 if( mnChunkLen < 4 )
810 return;
812 sal_uInt32 nGammaValue = ImplReadsal_uInt32();
813 double fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) );
814 double fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
816 if ( fInvGamma != 1.0 )
818 mbGamma = true;
820 if ( mpColorTable == mpDefaultColorTable )
821 mpColorTable = new sal_uInt8[ 256 ];
823 for ( sal_Int32 i = 0; i < 256; i++ )
824 mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5);
826 if ( mbGrayScale )
827 ImplGetGrayPalette( mnPngDepth );
831 void PNGReaderImpl::ImplGetBackground()
833 switch ( mnColorType )
835 case 3 :
837 if ( mnChunkLen == 1 )
839 sal_uInt16 nCol = *maDataIter++;
840 if ( nCol < mpAcc->GetPaletteEntryCount() )
842 mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) );
843 break;
847 break;
849 case 0 :
850 case 4 :
852 if ( mnChunkLen == 2 )
854 // the color type 0 and 4 is always greyscale,
855 // so the return value can be used as index
856 sal_uInt8 nIndex = ImplScaleColor();
857 mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) );
860 break;
862 case 2 :
863 case 6 :
865 if ( mnChunkLen == 6 )
867 sal_uInt8 nRed = ImplScaleColor();
868 sal_uInt8 nGreen = ImplScaleColor();
869 sal_uInt8 nBlue = ImplScaleColor();
870 mpAcc->Erase( Color( nRed, nGreen, nBlue ) );
873 break;
877 // for color type 0 and 4 (greyscale) the return value is always index to the color
878 // 2 and 6 (RGB) the return value is always the 8 bit color component
879 sal_uInt8 PNGReaderImpl::ImplScaleColor()
881 sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 );
882 sal_uInt16 nCol = ( *maDataIter++ << 8 );
884 nCol += *maDataIter++ & (sal_uInt16)nMask;
886 if ( mnPngDepth > 8 ) // convert 16bit graphics to 8
887 nCol >>= 8;
889 return (sal_uInt8) nCol;
892 // ImplReadIDAT reads as much image data as needed
894 void PNGReaderImpl::ImplReadIDAT()
896 if( mnChunkLen > 0 )
898 if ( !mbzCodecInUse )
900 mbzCodecInUse = true;
901 mpZCodec.BeginCompression( ZCODEC_NO_COMPRESSION, true );
903 mpZCodec.SetBreak( mnChunkLen );
904 SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, StreamMode::READ );
906 while ( ( mpZCodec.GetBreak() ) )
908 // get bytes needed to fill the current scanline
909 sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf);
910 sal_Int32 nRead = mpZCodec.ReadAsynchron( aIStrm, mpScanCurrent, nToRead );
911 if ( nRead < 0 )
913 mbStatus = false;
914 break;
916 if ( nRead < nToRead )
918 mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk
919 break;
921 else // this scanline is Finished
923 mpScanCurrent = mpInflateInBuf;
924 ImplApplyFilter();
926 ImplDrawScanline( mnXStart, mnXAdd );
927 mnYpos += mnYAdd;
930 if ( mnYpos >= (sal_uInt32)maOrigSize.Height() )
932 if( (mnPass < 7) && mnInterlaceType )
933 if( ImplPreparePass() )
934 continue;
935 mbIDAT = true;
936 break;
941 if( mbIDAT )
943 mpZCodec.EndCompression();
944 mbzCodecInUse = false;
948 bool PNGReaderImpl::ImplPreparePass()
950 struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; };
951 static const InterlaceParams aInterlaceParams[8] =
953 // non-interlaced
954 { 0, 0, 1, 1 },
955 // Adam7-interlaced
956 { 0, 0, 8, 8 }, // pass 1
957 { 4, 0, 8, 8 }, // pass 2
958 { 0, 4, 4, 8 }, // pass 3
959 { 2, 0, 4, 4 }, // pass 4
960 { 0, 2, 2, 4 }, // pass 5
961 { 1, 0, 2, 2 }, // pass 6
962 { 0, 1, 1, 2 } // pass 7
965 const InterlaceParams* pParam = &aInterlaceParams[ 0 ];
966 if( mnInterlaceType )
968 while( ++mnPass <= 7 )
970 pParam = &aInterlaceParams[ mnPass ];
972 // skip this pass if the original image is too small for it
973 if( (pParam->mnXStart < maOrigSize.Width())
974 && (pParam->mnYStart < maOrigSize.Height()) )
975 break;
977 if( mnPass > 7 )
978 return false;
980 // skip the last passes if possible (for scaled down target images)
981 if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) )
982 return false;
985 mnYpos = pParam->mnYStart;
986 mnXStart = pParam->mnXStart;
987 mnXAdd = pParam->mnXAdd;
988 mnYAdd = pParam->mnYAdd;
990 // in Interlace mode the size of scanline is not constant
991 // so first we calculate the number of entrys
992 long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd;
993 mnScansize = nScanWidth;
995 if( mbRGBTriple )
996 mnScansize = 3 * nScanWidth;
998 if( mbAlphaChannel )
999 mnScansize += nScanWidth;
1001 // convert to width in bytes
1002 mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3;
1004 ++mnScansize; // scan size also needs room for the filtertype byte
1005 memset( mpScanPrior, 0, mnScansize );
1007 return true;
1010 // ImplApplyFilter writes the complete Scanline (nY)
1011 // in interlace mode the parameter nXStart and nXAdd are non-zero
1013 void PNGReaderImpl::ImplApplyFilter()
1015 OSL_ASSERT( mnScansize >= mnBPP + 1 );
1016 const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize;
1018 sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline
1019 switch ( nFilterType )
1021 default: // unknown Scanline Filter Type
1022 case 0: // Filter Type "None"
1023 // we let the pixels pass and display the data unfiltered
1024 break;
1026 case 1: // Scanline Filter Type "Sub"
1028 sal_uInt8* p1 = mpInflateInBuf + 1;
1029 const sal_uInt8* p2 = p1;
1030 p1 += mnBPP;
1032 // use left pixels
1033 while (p1 < pScanEnd)
1035 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1036 ++p1;
1039 break;
1041 case 2: // Scanline Filter Type "Up"
1043 sal_uInt8* p1 = mpInflateInBuf + 1;
1044 const sal_uInt8* p2 = mpScanPrior + 1;
1046 // use pixels from prior line
1047 while( p1 < pScanEnd )
1049 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1050 ++p1;
1053 break;
1055 case 3: // Scanline Filter Type "Average"
1057 sal_uInt8* p1 = mpInflateInBuf + 1;
1058 const sal_uInt8* p2 = mpScanPrior + 1;
1059 const sal_uInt8* p3 = p1;
1061 // use one pixel from prior line
1062 for( int n = mnBPP; --n >= 0; ++p1, ++p2)
1063 *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) );
1065 // predict by averaging the left and prior line pixels
1066 while( p1 < pScanEnd )
1068 *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) );
1069 ++p1;
1072 break;
1074 case 4: // Scanline Filter Type "PaethPredictor"
1076 sal_uInt8* p1 = mpInflateInBuf + 1;
1077 const sal_uInt8* p2 = mpScanPrior + 1;
1078 const sal_uInt8* p3 = p1;
1079 const sal_uInt8* p4 = p2;
1081 // use one pixel from prior line
1082 for( int n = mnBPP; --n >= 0; ++p1)
1083 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1085 // predict by using the left and the prior line pixels
1086 while( p1 < pScanEnd )
1088 int na = *(p2++);
1089 int nb = *(p3++);
1090 int nc = *(p4++);
1092 int npa = nb - (int)nc;
1093 int npb = na - (int)nc;
1094 int npc = npa + npb;
1096 if( npa < 0 )
1097 npa =-npa;
1098 if( npb < 0 )
1099 npb =-npb;
1100 if( npc < 0 )
1101 npc =-npc;
1103 if( npa > npb )
1104 na = nb, npa = npb;
1105 if( npa > npc )
1106 na = nc;
1108 *p1 = static_cast<sal_uInt8>( *p1 + na );
1109 ++p1;
1112 break;
1115 memcpy( mpScanPrior, mpInflateInBuf, mnScansize );
1118 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1119 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1121 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd )
1123 // optimization for downscaling
1124 if( mnYpos & mnPreviewMask )
1125 return;
1126 if( nXStart & mnPreviewMask )
1127 return;
1129 // convert nY to pixel units in the target image
1130 // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1131 const sal_uInt32 nY = mnYpos >> mnPreviewShift;
1133 const sal_uInt8* pTmp = mpInflateInBuf + 1;
1134 if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1136 switch ( mpAcc->GetBitCount() )
1138 case 1 :
1140 if ( mbTransparent )
1142 for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1144 sal_uInt8 nCol;
1145 nShift = (nShift - 1) & 7;
1146 if ( nShift == 0 )
1147 nCol = *(pTmp++);
1148 else
1149 nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1150 nCol &= 1;
1152 ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1155 else
1156 { // BMP_FORMAT_1BIT_MSB_PAL
1157 for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1159 nShift = (nShift - 1) & 7;
1161 sal_uInt8 nCol;
1162 if ( nShift == 0 )
1163 nCol = *(pTmp++);
1164 else
1165 nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1166 nCol &= 1;
1168 ImplSetPixel( nY, nX, nCol );
1172 break;
1174 case 4 :
1176 if ( mbTransparent )
1178 if ( mnPngDepth == 4 ) // check if source has a two bit pixel format
1180 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex )
1182 if( nXIndex & 1 )
1184 ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] );
1185 pTmp++;
1187 else
1189 ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] );
1193 else // if ( mnPngDepth == 2 )
1195 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1197 sal_uInt8 nCol;
1198 switch( nXIndex & 3 )
1200 case 0 :
1201 nCol = *pTmp >> 6;
1202 break;
1204 case 1 :
1205 nCol = ( *pTmp >> 4 ) & 0x03 ;
1206 break;
1208 case 2 :
1209 nCol = ( *pTmp >> 2 ) & 0x03;
1210 break;
1212 case 3 :
1213 nCol = ( *pTmp++ ) & 0x03;
1214 break;
1216 default: // get rid of nCol uninitialized warning
1217 nCol = 0;
1218 break;
1221 ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1225 else
1227 if ( mnPngDepth == 4 ) // maybe the source is a two bitmap graphic
1228 { // BMP_FORMAT_4BIT_LSN_PAL
1229 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1231 if( nXIndex & 1 )
1232 ImplSetPixel( nY, nX, *pTmp++ & 0x0f );
1233 else
1234 ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f );
1237 else // if ( mnPngDepth == 2 )
1239 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1241 switch( nXIndex & 3 )
1243 case 0 :
1244 ImplSetPixel( nY, nX, *pTmp >> 6 );
1245 break;
1247 case 1 :
1248 ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 );
1249 break;
1251 case 2 :
1252 ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 );
1253 break;
1255 case 3 :
1256 ImplSetPixel( nY, nX, *pTmp++ & 0x03 );
1257 break;
1263 break;
1265 case 8 :
1267 if ( mbAlphaChannel )
1269 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1271 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1272 ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] );
1274 else
1276 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1277 ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] );
1280 else if ( mbTransparent )
1282 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1284 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ )
1285 ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1287 else
1289 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1290 ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1293 else // neither alpha nor transparency
1295 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1297 if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible
1299 int nLineBytes = maOrigSize.Width();
1300 mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes );
1301 pTmp += nLineBytes;
1303 else
1305 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd )
1306 ImplSetPixel( nY, nX, *pTmp++ );
1309 else
1311 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1312 ImplSetPixel( nY, nX, *pTmp );
1316 break;
1318 default :
1319 mbStatus = false;
1320 break;
1323 else // no palette => truecolor
1325 // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
1326 static bool bCkeckDirectScanline(true);
1328 if( mbAlphaChannel )
1330 // has RGB + alpha
1331 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1333 // BMP_FORMAT_32BIT_TC_RGBA
1334 // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha
1335 const bool bDoDirectScanline(
1336 bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpMaskAcc);
1337 const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1339 if(bDoDirectScanline)
1341 // allocate scanlines on demand, reused for next line
1342 if(!mpScanline)
1344 #if OSL_DEBUG_LEVEL > 0
1345 mnAllocSizeScanline = maOrigSize.Width() * 3;
1346 #endif
1347 mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1350 if(!mpScanlineAlpha)
1352 #if OSL_DEBUG_LEVEL > 0
1353 mnAllocSizeScanlineAlpha = maOrigSize.Width();
1354 #endif
1355 mpScanlineAlpha = new sal_uInt8[maOrigSize.Width()];
1359 if(bDoDirectScanline)
1361 OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1362 OSL_ENSURE(mpScanlineAlpha, "No ScanlineAlpha allocated (!)");
1363 OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1364 OSL_ENSURE(mnAllocSizeScanlineAlpha >= maOrigSize.Width(), "Allocated ScanlineAlpha too small (!)");
1365 sal_uInt8* pScanline(mpScanline);
1366 sal_uInt8* pScanlineAlpha(mpScanlineAlpha);
1368 for (sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 4)
1370 // prepare content line as BGR by reordering when copying
1371 // do not forget to invert alpha (source is alpha, target is opacity)
1372 if(bCustomColorTable)
1374 *pScanline++ = mpColorTable[pTmp[2]];
1375 *pScanline++ = mpColorTable[pTmp[1]];
1376 *pScanline++ = mpColorTable[pTmp[0]];
1377 *pScanlineAlpha++ = ~pTmp[3];
1379 else
1381 *pScanline++ = pTmp[2];
1382 *pScanline++ = pTmp[1];
1383 *pScanline++ = pTmp[0];
1384 *pScanlineAlpha++ = ~pTmp[3];
1388 // copy scanlines directly to bitmaps for content and alpha; use the formats which
1389 // are able to copy directly to BitmapBuffer
1390 mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3);
1391 mpMaskAcc->CopyScanline(nY, mpScanlineAlpha, BMP_FORMAT_8BIT_PAL, maOrigSize.Width());
1393 else
1395 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1397 if(bCustomColorTable)
1399 ImplSetAlphaPixel(
1402 BitmapColor(
1403 mpColorTable[ pTmp[ 0 ] ],
1404 mpColorTable[ pTmp[ 1 ] ],
1405 mpColorTable[ pTmp[ 2 ] ]),
1406 pTmp[ 3 ]);
1408 else
1410 ImplSetAlphaPixel(
1413 BitmapColor(
1414 pTmp[0],
1415 pTmp[1],
1416 pTmp[2]),
1417 pTmp[3]);
1422 else
1424 // BMP_FORMAT_64BIT_TC_RGBA
1425 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 )
1427 ImplSetAlphaPixel(
1430 BitmapColor(
1431 mpColorTable[ pTmp[ 0 ] ],
1432 mpColorTable[ pTmp[ 2 ] ],
1433 mpColorTable[ pTmp[ 4 ] ]),
1434 pTmp[6]);
1438 else if( mbTransparent ) // has RGB + transparency
1440 // BMP_FORMAT_24BIT_TC_RGB
1441 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1442 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1444 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1446 sal_uInt8 nRed = pTmp[ 0 ];
1447 sal_uInt8 nGreen = pTmp[ 1 ];
1448 sal_uInt8 nBlue = pTmp[ 2 ];
1449 bool bTransparent = ( ( nRed == mnTransRed )
1450 && ( nGreen == mnTransGreen )
1451 && ( nBlue == mnTransBlue ) );
1453 ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1454 mpColorTable[ nGreen ],
1455 mpColorTable[ nBlue ] ), bTransparent );
1458 else
1460 // BMP_FORMAT_48BIT_TC_RGB
1461 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1463 sal_uInt8 nRed = pTmp[ 0 ];
1464 sal_uInt8 nGreen = pTmp[ 2 ];
1465 sal_uInt8 nBlue = pTmp[ 4 ];
1466 bool bTransparent = ( ( nRed == mnTransRed )
1467 && ( nGreen == mnTransGreen )
1468 && ( nBlue == mnTransBlue ) );
1470 ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1471 mpColorTable[ nGreen ],
1472 mpColorTable[ nBlue ] ), bTransparent );
1476 else // has RGB but neither alpha nor transparency
1478 // BMP_FORMAT_24BIT_TC_RGB
1479 // only use DirectScanline when we have no preview shifting stuff and access to content
1480 const bool bDoDirectScanline(
1481 bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift);
1482 const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1484 if(bDoDirectScanline && !mpScanline)
1486 // allocate scanlines on demand, reused for next line
1487 #if OSL_DEBUG_LEVEL > 0
1488 mnAllocSizeScanline = maOrigSize.Width() * 3;
1489 #endif
1490 mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1493 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1495 if(bDoDirectScanline)
1497 OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1498 OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1499 sal_uInt8* pScanline(mpScanline);
1501 for (sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 3)
1503 // prepare content line as BGR by reordering when copying
1504 if(bCustomColorTable)
1506 *pScanline++ = mpColorTable[pTmp[2]];
1507 *pScanline++ = mpColorTable[pTmp[1]];
1508 *pScanline++ = mpColorTable[pTmp[0]];
1510 else
1512 *pScanline++ = pTmp[2];
1513 *pScanline++ = pTmp[1];
1514 *pScanline++ = pTmp[0];
1518 // copy scanline directly to bitmap for content; use the format which is able to
1519 // copy directly to BitmapBuffer
1520 mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3);
1522 else
1524 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1526 if(bCustomColorTable)
1528 ImplSetPixel(
1531 BitmapColor(
1532 mpColorTable[ pTmp[ 0 ] ],
1533 mpColorTable[ pTmp[ 1 ] ],
1534 mpColorTable[ pTmp[ 2 ] ]));
1536 else
1538 ImplSetPixel(
1541 BitmapColor(
1542 pTmp[0],
1543 pTmp[1],
1544 pTmp[2]));
1549 else
1551 // BMP_FORMAT_48BIT_TC_RGB
1552 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1553 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1555 ImplSetPixel(
1558 BitmapColor(
1559 mpColorTable[ pTmp[ 0 ] ],
1560 mpColorTable[ pTmp[ 2 ] ],
1561 mpColorTable[ pTmp[ 4 ] ]));
1568 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor )
1570 // TODO: get preview mode checks out of inner loop
1571 if( nX & mnPreviewMask )
1572 return;
1573 nX >>= mnPreviewShift;
1575 mpAcc->SetPixel( nY, nX, rBitmapColor );
1578 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex )
1580 // TODO: get preview mode checks out of inner loop
1581 if( nX & mnPreviewMask )
1582 return;
1583 nX >>= mnPreviewShift;
1585 mpAcc->SetPixelIndex( nY, nX, nPalIndex );
1588 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, bool bTrans )
1590 // TODO: get preview mode checks out of inner loop
1591 if( nX & mnPreviewMask )
1592 return;
1593 nX >>= mnPreviewShift;
1595 mpAcc->SetPixel( nY, nX, rBitmapColor );
1597 if ( bTrans )
1598 mpMaskAcc->SetPixel( nY, nX, mcTranspColor );
1599 else
1600 mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor );
1603 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1604 sal_uInt8 nPalIndex, sal_uInt8 nAlpha )
1606 // TODO: get preview mode checks out of inner loop
1607 if( nX & mnPreviewMask )
1608 return;
1609 nX >>= mnPreviewShift;
1611 mpAcc->SetPixelIndex( nY, nX, nPalIndex );
1612 mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha );
1615 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1616 const BitmapColor& rBitmapColor, sal_uInt8 nAlpha )
1618 // TODO: get preview mode checks out of inner loop
1619 if( nX & mnPreviewMask )
1620 return;
1621 nX >>= mnPreviewShift;
1623 mpAcc->SetPixel( nY, nX, rBitmapColor );
1624 if (!mpMaskAcc)
1625 return;
1626 mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha );
1629 sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32()
1631 sal_uInt32 nRet;
1632 nRet = *maDataIter++;
1633 nRet <<= 8;
1634 nRet |= *maDataIter++;
1635 nRet <<= 8;
1636 nRet |= *maDataIter++;
1637 nRet <<= 8;
1638 nRet |= *maDataIter++;
1639 return nRet;
1642 PNGReader::PNGReader(SvStream& rIStream) :
1643 mpImpl(new vcl::PNGReaderImpl(rIStream))
1647 PNGReader::~PNGReader()
1651 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint )
1653 return mpImpl->GetBitmapEx( i_rPreviewSizeHint );
1656 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const
1658 return mpImpl->GetAllChunks();
1661 void PNGReader::SetIgnoreGammaChunk(bool bIgnoreGammaChunk)
1663 mpImpl->SetIgnoreGammaChunk(bIgnoreGammaChunk);
1666 } // namespace vcl
1668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */