Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / pngread.cxx
blob40d4085f56ffdeb99550ab3b2bbab6745a405aae
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 <memory>
21 #include <vcl/pngread.hxx>
23 #include <cmath>
24 #include <rtl/crc.h>
25 #include <rtl/alloc.h>
26 #include <tools/zcodec.hxx>
27 #include <tools/stream.hxx>
28 #include <vcl/bitmapaccess.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/alpha.hxx>
31 #include <osl/endian.h>
32 #include <o3tl/make_unique.hxx>
34 namespace vcl
37 #define PNGCHUNK_IHDR 0x49484452
38 #define PNGCHUNK_PLTE 0x504c5445
39 #define PNGCHUNK_IDAT 0x49444154
40 #define PNGCHUNK_IEND 0x49454e44
41 #define PNGCHUNK_bKGD 0x624b4744
42 #define PNGCHUNK_gAMA 0x67414d41
43 #define PNGCHUNK_pHYs 0x70485973
44 #define PNGCHUNK_tRNS 0x74524e53
46 #define VIEWING_GAMMA 2.35
47 #define DISPLAY_GAMMA 1.0
50 static const sal_uInt8 mpDefaultColorTable[ 256 ] =
51 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
52 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
53 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
54 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
55 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
58 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
59 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
60 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
62 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
63 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
64 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
65 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
66 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
69 class PNGReaderImpl
71 private:
72 SvStream& mrPNGStream;
73 SvStreamEndian mnOrigStreamMode;
75 std::vector<vcl::PNGReader::ChunkData> maChunkSeq;
76 std::vector<vcl::PNGReader::ChunkData>::iterator maChunkIter;
77 std::vector<sal_uInt8>::iterator maDataIter;
79 std::unique_ptr<Bitmap> mpBmp;
80 Bitmap::ScopedWriteAccess mxAcc;
81 std::unique_ptr<Bitmap> mpMaskBmp;
82 Bitmap::ScopedWriteAccess mxMaskAcc;
83 std::unique_ptr<AlphaMask> mpAlphaMask;
84 AlphaMask::ScopedWriteAccess mxAlphaAcc;
85 BitmapWriteAccess* mpMaskAcc;
87 ZCodec mpZCodec;
88 sal_uInt8* mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1
89 sal_uInt8* mpScanPrior; // pointer to the latest scanline
90 sal_uInt8* mpTransTab; // for transparency in images with palette colortype
91 sal_uInt8* mpScanCurrent; // pointer into the current scanline
92 sal_uInt8* mpColorTable;
93 std::size_t mnStreamSize; // estimate of PNG file size
94 sal_uInt32 mnChunkType; // Type of current PNG chunk
95 sal_Int32 mnChunkLen; // Length of current PNG chunk
96 Size maOrigSize; // pixel size of the full image
97 Size maTargetSize; // pixel size of the result image
98 Size maPhysSize; // preferred size in MapUnit::Map100thMM units
99 sal_uInt32 mnBPP; // number of bytes per pixel
100 sal_uInt32 mnScansize; // max size of scanline
101 sal_uInt32 mnYpos; // latest y position in full image
102 int mnPass; // if interlaced the latest pass ( 1..7 ) else 7
103 sal_uInt32 mnXStart; // the starting X for the current pass
104 sal_uInt32 mnXAdd; // the increment for input images X coords for the current pass
105 sal_uInt32 mnYAdd; // the increment for input images Y coords for the current pass
106 int mnPreviewShift; // shift to convert orig image coords into preview image coords
107 int mnPreviewMask; // == ((1 << mnPreviewShift) - 1)
108 sal_uInt16 mnTargetDepth; // pixel depth of target bitmap
109 sal_uInt8 mnTransRed;
110 sal_uInt8 mnTransGreen;
111 sal_uInt8 mnTransBlue;
112 sal_uInt8 mnPngDepth; // pixel depth of PNG data
113 sal_uInt8 mnColorType;
114 sal_uInt8 mnCompressionType;
115 sal_uInt8 mnFilterType;
116 sal_uInt8 mnInterlaceType;
117 BitmapColor mcTranspColor; // transparency mask's transparency "color"
118 BitmapColor mcOpaqueColor; // transparency mask's opaque "color"
119 bool mbTransparent : 1; // graphic includes an tRNS Chunk or an alpha Channel
120 bool mbAlphaChannel : 1; // is true for ColorType 4 and 6
121 bool mbRGBTriple : 1;
122 bool mbPalette : 1; // false if we need a Palette
123 bool mbGrayScale : 1;
124 bool mbzCodecInUse : 1;
125 bool mbStatus : 1;
126 bool mbIDAT : 1; // true if finished with enough IDAT chunks
127 bool mbGamma : 1; // true if Gamma Correction available
128 bool mbpHYs : 1; // true if physical size of pixel available
129 bool mbIgnoreGammaChunk : 1;
131 #if OSL_DEBUG_LEVEL > 0
132 // do some checks in debug mode
133 sal_Int32 mnAllocSizeScanline;
134 sal_Int32 mnAllocSizeScanlineAlpha;
135 #endif
136 // the temporary Scanline (and alpha) for direct scanline copy to Bitmap
137 sal_uInt8* mpScanline;
138 sal_uInt8* mpScanlineAlpha;
140 bool ReadNextChunk();
141 void ReadRemainingChunks();
143 void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & );
144 void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex );
145 void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, bool bTrans );
146 void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha );
147 void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha );
148 void ImplReadIDAT();
149 bool ImplPreparePass();
150 void ImplApplyFilter();
151 void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd );
152 bool ImplReadTransparent();
153 void ImplGetGamma();
154 void ImplGetBackground();
155 sal_uInt8 ImplScaleColor();
156 bool ImplReadHeader( const Size& rPreviewSizeHint );
157 bool ImplReadPalette();
158 void ImplGetGrayPalette( sal_uInt16 );
159 sal_uInt32 ImplReadsal_uInt32();
161 public:
163 explicit PNGReaderImpl( SvStream& );
164 ~PNGReaderImpl();
166 BitmapEx GetBitmapEx( const Size& rPreviewSizeHint );
167 const std::vector<vcl::PNGReader::ChunkData>& GetAllChunks();
168 void SetIgnoreGammaChunk( bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; };
171 PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream )
172 : mrPNGStream( rPNGStream ),
173 mpMaskAcc ( nullptr ),
174 mpInflateInBuf ( nullptr ),
175 mpScanPrior ( nullptr ),
176 mpTransTab ( nullptr ),
177 mpScanCurrent ( nullptr ),
178 mpColorTable ( const_cast<sal_uInt8*>(mpDefaultColorTable) ),
179 mnChunkType ( 0 ),
180 mnChunkLen ( 0 ),
181 mnBPP ( 0 ),
182 mnScansize ( 0 ),
183 mnYpos ( 0 ),
184 mnPass ( 0 ),
185 mnXStart ( 0 ),
186 mnXAdd ( 0 ),
187 mnYAdd ( 0 ),
188 mnTargetDepth ( 0 ),
189 mnTransRed ( 0 ),
190 mnTransGreen ( 0 ),
191 mnTransBlue ( 0 ),
192 mnPngDepth ( 0 ),
193 mnColorType ( 0 ),
194 mnCompressionType( 0 ),
195 mnFilterType ( 0 ),
196 mnInterlaceType ( 0 ),
197 mbTransparent( false ),
198 mbAlphaChannel( false ),
199 mbRGBTriple( false ),
200 mbPalette( false ),
201 mbGrayScale( false ),
202 mbzCodecInUse ( false ),
203 mbStatus( true ),
204 mbIDAT( false ),
205 mbGamma ( false ),
206 mbpHYs ( false ),
207 mbIgnoreGammaChunk ( false ),
208 #if OSL_DEBUG_LEVEL > 0
209 mnAllocSizeScanline(0),
210 mnAllocSizeScanlineAlpha(0),
211 #endif
212 mpScanline(nullptr),
213 mpScanlineAlpha(nullptr)
215 // prepare the PNG data stream
216 mnOrigStreamMode = mrPNGStream.GetEndian();
217 mrPNGStream.SetEndian( SvStreamEndian::BIG );
219 // prepare the chunk reader
220 maChunkSeq.reserve( 16 );
221 maChunkIter = maChunkSeq.begin();
223 // estimate PNG file size (to allow sanity checks)
224 const std::size_t nStreamPos = mrPNGStream.Tell();
225 mrPNGStream.Seek( STREAM_SEEK_TO_END );
226 mnStreamSize = mrPNGStream.Tell();
227 mrPNGStream.Seek( nStreamPos );
229 // check the PNG header magic
230 sal_uInt32 nDummy = 0;
231 mrPNGStream.ReadUInt32( nDummy );
232 mbStatus = (nDummy == 0x89504e47);
233 mrPNGStream.ReadUInt32( nDummy );
234 mbStatus = (nDummy == 0x0d0a1a0a) && mbStatus;
236 mnPreviewShift = 0;
237 mnPreviewMask = (1 << mnPreviewShift) - 1;
240 PNGReaderImpl::~PNGReaderImpl()
242 mrPNGStream.SetEndian( mnOrigStreamMode );
244 if ( mbzCodecInUse )
245 mpZCodec.EndCompression();
247 if( mpColorTable != mpDefaultColorTable )
248 delete[] mpColorTable;
250 delete[] mpTransTab;
251 delete[] mpInflateInBuf;
252 delete[] mpScanPrior;
254 delete[] mpScanline;
255 delete[] mpScanlineAlpha;
258 bool PNGReaderImpl::ReadNextChunk()
260 if( maChunkIter == maChunkSeq.end() )
262 // get the next chunk from the stream
264 // unless we are at the end of the PNG stream
265 if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) )
266 return false;
267 if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
268 return false;
270 PNGReader::ChunkData aDummyChunk;
271 maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk );
272 PNGReader::ChunkData& rChunkData = *maChunkIter;
274 // read the chunk header
275 mrPNGStream.ReadInt32( mnChunkLen ).ReadUInt32( mnChunkType );
276 rChunkData.nType = mnChunkType;
278 // fdo#61847 truncate over-long, trailing chunks
279 const std::size_t nStreamPos = mrPNGStream.Tell();
280 if( mnChunkLen < 0 || nStreamPos + mnChunkLen >= mnStreamSize )
281 mnChunkLen = mnStreamSize - nStreamPos;
283 // calculate chunktype CRC (swap it back to original byte order)
284 sal_uInt32 nChunkType = mnChunkType;
285 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
286 nChunkType = OSL_SWAPDWORD( nChunkType );
287 #endif
288 sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 );
290 // read the chunk data and check the CRC
291 if( mnChunkLen && !mrPNGStream.IsEof() )
293 rChunkData.aData.resize( mnChunkLen );
295 sal_Int32 nBytesRead = 0;
296 do {
297 sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ];
298 nBytesRead += mrPNGStream.ReadBytes(pPtr, mnChunkLen - nBytesRead);
299 } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) );
301 nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen );
302 maDataIter = rChunkData.aData.begin();
304 sal_uInt32 nCheck(0);
305 mrPNGStream.ReadUInt32( nCheck );
306 if( nCRC32 != nCheck )
307 return false;
309 else
311 // the next chunk was already read
312 mnChunkType = (*maChunkIter).nType;
313 mnChunkLen = (*maChunkIter).aData.size();
314 maDataIter = (*maChunkIter).aData.begin();
317 ++maChunkIter;
318 return mnChunkType != PNGCHUNK_IEND;
321 // read the remaining chunks from mrPNGStream
322 void PNGReaderImpl::ReadRemainingChunks()
324 while( ReadNextChunk() ) ;
327 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks()
329 ReadRemainingChunks();
330 return maChunkSeq;
333 BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint )
335 // reset to the first chunk
336 maChunkIter = maChunkSeq.begin();
338 // first chunk must be IDHR
339 if( mbStatus && ReadNextChunk() )
341 if (mnChunkType == PNGCHUNK_IHDR)
342 mbStatus = ImplReadHeader( rPreviewSizeHint );
343 else
344 mbStatus = false;
347 // parse the remaining chunks
348 while (mbStatus && !mbIDAT && ReadNextChunk())
350 switch( mnChunkType )
352 case PNGCHUNK_IHDR :
354 mbStatus = false; //IHDR should only appear as the first chunk
356 break;
358 case PNGCHUNK_gAMA : // the gamma chunk must precede
359 { // the 'IDAT' and also the 'PLTE'(if available )
360 if ( !mbIgnoreGammaChunk && !mbIDAT )
361 ImplGetGamma();
363 break;
365 case PNGCHUNK_PLTE :
367 if ( !mbPalette )
368 mbStatus = ImplReadPalette();
370 break;
372 case PNGCHUNK_tRNS :
374 if ( !mbIDAT ) // the tRNS chunk must precede the IDAT
375 mbStatus = ImplReadTransparent();
377 break;
379 case PNGCHUNK_bKGD : // the background chunk must appear
381 if ( !mbIDAT && mbPalette ) // before the 'IDAT' and after the
382 ImplGetBackground(); // PLTE(if available ) chunk.
384 break;
386 case PNGCHUNK_IDAT :
388 if ( !mpInflateInBuf ) // taking care that the header has properly been read
389 mbStatus = false;
390 else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes
391 ImplReadIDAT();
393 break;
395 case PNGCHUNK_pHYs :
397 if ( !mbIDAT && mnChunkLen == 9 )
399 sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32();
400 sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32();
402 sal_uInt8 nUnitSpecifier = *maDataIter++;
403 if( (nUnitSpecifier == 1) && nXPixelPerMeter && nYPixelPerMeter )
405 mbpHYs = true;
407 // convert into MapUnit::Map100thMM
408 maPhysSize.Width() = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter );
409 maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter );
413 break;
415 case PNGCHUNK_IEND:
416 mbStatus = mbIDAT; // there is a problem if the image is not complete yet
417 break;
421 // release write access of the bitmaps
422 mxAcc.reset();
423 mxMaskAcc.reset();
424 mxAlphaAcc.reset();
425 mpMaskAcc = nullptr;
427 // return the resulting BitmapEx
428 BitmapEx aRet;
430 if( !mbStatus || !mbIDAT )
431 aRet.Clear();
432 else
434 if ( mpAlphaMask )
435 aRet = BitmapEx( *mpBmp, *mpAlphaMask );
436 else if ( mpMaskBmp )
437 aRet = BitmapEx( *mpBmp, *mpMaskBmp );
438 else
439 aRet = *mpBmp;
441 if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() )
443 aRet.SetPrefMapMode( MapUnit::Map100thMM );
444 aRet.SetPrefSize( maPhysSize );
447 return aRet;
450 bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint )
452 if( mnChunkLen < 13 )
453 return false;
455 maOrigSize.Width() = ImplReadsal_uInt32();
456 maOrigSize.Height() = ImplReadsal_uInt32();
458 if (maOrigSize.Width() <= 0 || maOrigSize.Height() <= 0)
459 return false;
461 mnPngDepth = *(maDataIter++);
462 mnColorType = *(maDataIter++);
464 mnCompressionType = *(maDataIter++);
465 if( mnCompressionType != 0 ) // unknown compression type
466 return false;
468 mnFilterType = *(maDataIter++);
469 if( mnFilterType != 0 ) // unknown filter type
470 return false;
472 mnInterlaceType = *(maDataIter++);
473 switch ( mnInterlaceType ) // filter type valid ?
475 case 0 : // progressive image
476 mnPass = 7;
477 break;
478 case 1 : // Adam7-interlaced image
479 mnPass = 0;
480 break;
481 default:
482 return false;
485 mbPalette = true;
486 mbIDAT = mbAlphaChannel = mbTransparent = false;
487 mbGrayScale = mbRGBTriple = false;
488 mnTargetDepth = mnPngDepth;
489 sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3;
491 // valid color types are 0,2,3,4 & 6
492 switch ( mnColorType )
494 case 0 : // each pixel is a grayscale
496 switch ( mnPngDepth )
498 case 2 : // 2bit target not available -> use four bits
499 mnTargetDepth = 4; // we have to expand the bitmap
500 mbGrayScale = true;
501 break;
502 case 16 :
503 mnTargetDepth = 8; // we have to reduce the bitmap
504 SAL_FALLTHROUGH;
505 case 1 :
506 case 4 :
507 case 8 :
508 mbGrayScale = true;
509 break;
510 default :
511 return false;
514 break;
516 case 2 : // each pixel is an RGB triple
518 mbRGBTriple = true;
519 nScansize64 *= 3;
520 switch ( mnPngDepth )
522 case 16 : // we have to reduce the bitmap
523 case 8 :
524 mnTargetDepth = 24;
525 break;
526 default :
527 return false;
530 break;
532 case 3 : // each pixel is a palette index
534 switch ( mnPngDepth )
536 case 2 :
537 mnTargetDepth = 4; // we have to expand the bitmap
538 SAL_FALLTHROUGH;
539 case 1 :
540 case 4 :
541 case 8 :
542 mbPalette = false;
543 break;
544 default :
545 return false;
548 break;
550 case 4 : // each pixel is a grayscale sample followed by an alpha sample
552 nScansize64 *= 2;
553 mbAlphaChannel = true;
554 switch ( mnPngDepth )
556 case 16 :
557 mnTargetDepth = 8; // we have to reduce the bitmap
558 SAL_FALLTHROUGH;
559 case 8 :
560 mbGrayScale = true;
561 break;
562 default :
563 return false;
566 break;
568 case 6 : // each pixel is an RGB triple followed by an alpha sample
570 mbRGBTriple = true;
571 nScansize64 *= 4;
572 mbAlphaChannel = true;
573 switch (mnPngDepth )
575 case 16 : // we have to reduce the bitmap
576 case 8 :
577 mnTargetDepth = 24;
578 break;
579 default :
580 return false;
583 break;
585 default :
586 return false;
589 mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() );
590 if ( !mnBPP )
591 mnBPP = 1;
593 nScansize64++; // each scanline includes one filterbyte
595 if ( nScansize64 > SAL_MAX_UINT32 )
596 return false;
598 // assume max theoretical compression of 1:1032
599 sal_uInt64 nMinSizeRequired = (nScansize64 * maOrigSize.Height()) / 1032;
600 if (nMinSizeRequired > mnStreamSize)
602 SAL_WARN("vcl.gdi", "overlarge png dimensions: " <<
603 maOrigSize.Width() << " x " << maOrigSize.Height() << " depth: " << (int)mnPngDepth <<
604 " couldn't be supplied by file length " << mnStreamSize << " at least " << nMinSizeRequired << " needed ");
605 return false;
608 mnScansize = static_cast< sal_uInt32 >( nScansize64 );
610 // calculate target size from original size and the preview hint
611 if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() )
613 Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() );
614 maTargetSize = maOrigSize;
616 if( aPreviewSize.Width() == 0 ) {
617 aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() );
618 if( aPreviewSize.Width() <= 0 )
619 aPreviewSize.setWidth( 1 );
620 } else if( aPreviewSize.Height() == 0 ) {
621 aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() );
622 if( aPreviewSize.Height() <= 0 )
623 aPreviewSize.setHeight( 1 );
626 if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) {
627 SAL_INFO( "vcl.gdi", "preview size " << aPreviewSize.Width() << " " << aPreviewSize.Height() );
629 for( int i = 1; i < 5; ++i )
631 if( (maTargetSize.Width() >> i) < aPreviewSize.Width() )
632 break;
633 if( (maTargetSize.Height() >> i) < aPreviewSize.Height() )
634 break;
635 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 = o3tl::make_unique<Bitmap>( maTargetSize, mnTargetDepth );
665 mxAcc = Bitmap::ScopedWriteAccess(*mpBmp);
666 if (!mxAcc)
667 return false;
669 if ( mbAlphaChannel )
671 mpAlphaMask = o3tl::make_unique<AlphaMask>( maTargetSize );
672 mpAlphaMask->Erase( 128 );
673 mxAlphaAcc = AlphaMask::ScopedWriteAccess(*mpAlphaMask);
674 mpMaskAcc = mxAlphaAcc.get();
675 if (!mpMaskAcc)
676 return false;
679 if ( mbGrayScale )
680 ImplGetGrayPalette( mnPngDepth );
682 ImplPreparePass();
684 return true;
687 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth )
689 if( nBitDepth > 8 )
690 nBitDepth = 8;
692 sal_uInt16 nPaletteEntryCount = 1 << nBitDepth;
693 sal_uInt32 nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0;
695 // no bitdepth==2 available
696 // but bitdepth==4 with two unused bits is close enough
697 if( nBitDepth == 2 )
698 nPaletteEntryCount = 16;
700 mxAcc->SetPaletteEntryCount( nPaletteEntryCount );
701 for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd )
702 mxAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ],
703 mpColorTable[ nStart ], mpColorTable[ nStart ] ) );
706 bool PNGReaderImpl::ImplReadPalette()
708 sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 );
710 if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mxAcc )
712 mbPalette = true;
713 mxAcc->SetPaletteEntryCount( nCount );
715 for ( sal_uInt16 i = 0; i < nCount; i++ )
717 sal_uInt8 nRed = mpColorTable[ *maDataIter++ ];
718 sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ];
719 sal_uInt8 nBlue = mpColorTable[ *maDataIter++ ];
720 mxAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) );
723 else
724 mbStatus = false;
726 return mbStatus;
729 bool PNGReaderImpl::ImplReadTransparent()
731 bool bNeedAlpha = false;
733 if ( mpTransTab == nullptr )
735 switch ( mnColorType )
737 case 0 :
739 if ( mnChunkLen == 2 )
741 mpTransTab = new sal_uInt8[ 256 ];
742 memset( mpTransTab, 0xff, 256);
743 // color type 0 and 4 is always greyscale,
744 // so the return value can be used as index
745 sal_uInt8 nIndex = ImplScaleColor();
746 mpTransTab[ nIndex ] = 0;
747 mbTransparent = true;
750 break;
752 case 2 :
754 if ( mnChunkLen == 6 )
756 mnTransRed = ImplScaleColor();
757 mnTransGreen = ImplScaleColor();
758 mnTransBlue = ImplScaleColor();
759 mbTransparent = true;
762 break;
764 case 3 :
766 if ( mnChunkLen <= 256 )
768 mbTransparent = true;
769 mpTransTab = new sal_uInt8 [ 256 ];
770 memset( mpTransTab, 0xff, 256 );
771 if (mnChunkLen > 0)
773 memcpy( mpTransTab, &(*maDataIter), mnChunkLen );
774 maDataIter += mnChunkLen;
775 // need alpha transparency if not on/off masking
776 for( int i = 0; i < mnChunkLen; ++i )
777 bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF);
781 break;
785 if( mbTransparent && !mbAlphaChannel && !mpMaskBmp )
787 if( bNeedAlpha)
789 mpAlphaMask = o3tl::make_unique<AlphaMask>( maTargetSize );
790 mxAlphaAcc = AlphaMask::ScopedWriteAccess(*mpAlphaMask);
791 mpMaskAcc = mxAlphaAcc.get();
793 else
795 mpMaskBmp = o3tl::make_unique<Bitmap>( maTargetSize, 1 );
796 mxMaskAcc = Bitmap::ScopedWriteAccess(*mpMaskBmp);
797 mpMaskAcc = mxMaskAcc.get();
799 mbTransparent = (mpMaskAcc != nullptr);
800 if( !mbTransparent )
801 return false;
802 mcOpaqueColor = BitmapColor( 0x00 );
803 mcTranspColor = BitmapColor( 0xFF );
804 mpMaskAcc->Erase( 0x00 );
807 return true;
810 void PNGReaderImpl::ImplGetGamma()
812 if( mnChunkLen < 4 )
813 return;
815 sal_uInt32 nGammaValue = ImplReadsal_uInt32();
816 double fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) );
817 double fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
819 if ( fInvGamma != 1.0 )
821 mbGamma = true;
823 if ( mpColorTable == mpDefaultColorTable )
824 mpColorTable = new sal_uInt8[ 256 ];
826 for ( sal_Int32 i = 0; i < 256; i++ )
827 mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5);
829 if ( mbGrayScale )
830 ImplGetGrayPalette( mnPngDepth );
834 void PNGReaderImpl::ImplGetBackground()
836 switch ( mnColorType )
838 case 3 :
840 if ( mnChunkLen == 1 )
842 sal_uInt16 nCol = *maDataIter++;
843 if ( nCol < mxAcc->GetPaletteEntryCount() )
845 mxAcc->Erase( mxAcc->GetPaletteColor( (sal_uInt8)nCol ) );
846 break;
850 break;
852 case 0 :
853 case 4 :
855 if ( mnChunkLen == 2 )
857 // the color type 0 and 4 is always greyscale,
858 // so the return value can be used as index
859 sal_uInt8 nIndex = ImplScaleColor();
860 mxAcc->Erase( mxAcc->GetPaletteColor( nIndex ) );
863 break;
865 case 2 :
866 case 6 :
868 if ( mnChunkLen == 6 )
870 sal_uInt8 nRed = ImplScaleColor();
871 sal_uInt8 nGreen = ImplScaleColor();
872 sal_uInt8 nBlue = ImplScaleColor();
873 mxAcc->Erase( Color( nRed, nGreen, nBlue ) );
876 break;
880 // for color type 0 and 4 (greyscale) the return value is always index to the color
881 // 2 and 6 (RGB) the return value is always the 8 bit color component
882 sal_uInt8 PNGReaderImpl::ImplScaleColor()
884 sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 );
885 sal_uInt16 nCol = ( *maDataIter++ << 8 );
887 nCol += *maDataIter++ & (sal_uInt16)nMask;
889 if ( mnPngDepth > 8 ) // convert 16bit graphics to 8
890 nCol >>= 8;
892 return (sal_uInt8) nCol;
895 // ImplReadIDAT reads as much image data as needed
897 void PNGReaderImpl::ImplReadIDAT()
899 if( mnChunkLen > 0 )
901 if ( !mbzCodecInUse )
903 mbzCodecInUse = true;
904 mpZCodec.BeginCompression( ZCODEC_NO_COMPRESSION, true );
906 mpZCodec.SetBreak( mnChunkLen );
907 SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, StreamMode::READ );
909 while ( ( mpZCodec.GetBreak() ) )
911 // get bytes needed to fill the current scanline
912 sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf);
913 sal_Int32 nRead = mpZCodec.ReadAsynchron( aIStrm, mpScanCurrent, nToRead );
914 if ( nRead < 0 )
916 mbStatus = false;
917 break;
919 if ( nRead < nToRead )
921 mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk
922 break;
924 else // this scanline is Finished
926 mpScanCurrent = mpInflateInBuf;
927 ImplApplyFilter();
929 ImplDrawScanline( mnXStart, mnXAdd );
930 mnYpos += mnYAdd;
933 if ( mnYpos >= (sal_uInt32)maOrigSize.Height() )
935 if( (mnPass < 7) && mnInterlaceType )
936 if( ImplPreparePass() )
937 continue;
938 mbIDAT = true;
939 break;
944 if( mbIDAT )
946 mpZCodec.EndCompression();
947 mbzCodecInUse = false;
951 bool PNGReaderImpl::ImplPreparePass()
953 struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; };
954 static const InterlaceParams aInterlaceParams[8] =
956 // non-interlaced
957 { 0, 0, 1, 1 },
958 // Adam7-interlaced
959 { 0, 0, 8, 8 }, // pass 1
960 { 4, 0, 8, 8 }, // pass 2
961 { 0, 4, 4, 8 }, // pass 3
962 { 2, 0, 4, 4 }, // pass 4
963 { 0, 2, 2, 4 }, // pass 5
964 { 1, 0, 2, 2 }, // pass 6
965 { 0, 1, 1, 2 } // pass 7
968 const InterlaceParams* pParam = &aInterlaceParams[ 0 ];
969 if( mnInterlaceType )
971 while( ++mnPass <= 7 )
973 pParam = &aInterlaceParams[ mnPass ];
975 // skip this pass if the original image is too small for it
976 if( (pParam->mnXStart < maOrigSize.Width())
977 && (pParam->mnYStart < maOrigSize.Height()) )
978 break;
980 if( mnPass > 7 )
981 return false;
983 // skip the last passes if possible (for scaled down target images)
984 if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) )
985 return false;
988 mnYpos = pParam->mnYStart;
989 mnXStart = pParam->mnXStart;
990 mnXAdd = pParam->mnXAdd;
991 mnYAdd = pParam->mnYAdd;
993 // in Interlace mode the size of scanline is not constant
994 // so first we calculate the number of entrys
995 long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd;
996 mnScansize = nScanWidth;
998 if( mbRGBTriple )
999 mnScansize = 3 * nScanWidth;
1001 if( mbAlphaChannel )
1002 mnScansize += nScanWidth;
1004 // convert to width in bytes
1005 mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3;
1007 ++mnScansize; // scan size also needs room for the filtertype byte
1008 memset( mpScanPrior, 0, mnScansize );
1010 return true;
1013 // ImplApplyFilter writes the complete Scanline (nY)
1014 // in interlace mode the parameter nXStart and nXAdd are non-zero
1016 void PNGReaderImpl::ImplApplyFilter()
1018 OSL_ASSERT( mnScansize >= mnBPP + 1 );
1019 const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize;
1021 sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline
1022 switch ( nFilterType )
1024 default: // unknown Scanline Filter Type
1025 case 0: // Filter Type "None"
1026 // we let the pixels pass and display the data unfiltered
1027 break;
1029 case 1: // Scanline Filter Type "Sub"
1031 sal_uInt8* p1 = mpInflateInBuf + 1;
1032 const sal_uInt8* p2 = p1;
1033 p1 += mnBPP;
1035 // use left pixels
1036 while (p1 < pScanEnd)
1038 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1039 ++p1;
1042 break;
1044 case 2: // Scanline Filter Type "Up"
1046 sal_uInt8* p1 = mpInflateInBuf + 1;
1047 const sal_uInt8* p2 = mpScanPrior + 1;
1049 // use pixels from prior line
1050 while( p1 < pScanEnd )
1052 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1053 ++p1;
1056 break;
1058 case 3: // Scanline Filter Type "Average"
1060 sal_uInt8* p1 = mpInflateInBuf + 1;
1061 const sal_uInt8* p2 = mpScanPrior + 1;
1062 const sal_uInt8* p3 = p1;
1064 // use one pixel from prior line
1065 for( int n = mnBPP; --n >= 0; ++p1, ++p2)
1066 *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) );
1068 // predict by averaging the left and prior line pixels
1069 while( p1 < pScanEnd )
1071 *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) );
1072 ++p1;
1075 break;
1077 case 4: // Scanline Filter Type "PathPredictor"
1079 sal_uInt8* p1 = mpInflateInBuf + 1;
1080 const sal_uInt8* p2 = mpScanPrior + 1;
1081 const sal_uInt8* p3 = p1;
1082 const sal_uInt8* p4 = p2;
1084 // use one pixel from prior line
1085 for( int n = mnBPP; --n >= 0; ++p1)
1086 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1088 // predict by using the left and the prior line pixels
1089 while( p1 < pScanEnd )
1091 int na = *(p2++);
1092 int nb = *(p3++);
1093 int nc = *(p4++);
1095 int npa = nb - nc;
1096 int npb = na - nc;
1097 int npc = npa + npb;
1099 if( npa < 0 )
1100 npa =-npa;
1101 if( npb < 0 )
1102 npb =-npb;
1103 if( npc < 0 )
1104 npc =-npc;
1106 if( npa > npb )
1108 na = nb;
1109 npa = npb;
1111 if( npa > npc )
1112 na = nc;
1114 *p1 = static_cast<sal_uInt8>( *p1 + na );
1115 ++p1;
1118 break;
1121 memcpy( mpScanPrior, mpInflateInBuf, mnScansize );
1124 namespace
1126 sal_uInt8 SanitizePaletteIndex(sal_uInt8 nIndex, sal_uInt16 nPaletteEntryCount)
1128 if (nIndex >= nPaletteEntryCount)
1130 auto nSanitizedIndex = nIndex % nPaletteEntryCount;
1131 SAL_WARN_IF(nIndex != nSanitizedIndex, "vcl", "invalid colormap index: "
1132 << static_cast<unsigned int>(nIndex) << ", colormap len is: "
1133 << nPaletteEntryCount);
1134 nIndex = nSanitizedIndex;
1136 return nIndex;
1139 void SanitizePaletteIndexes(sal_uInt8* pEntries, int nLen, const Bitmap::ScopedWriteAccess& rAcc)
1141 sal_uInt16 nPaletteEntryCount = rAcc->GetPaletteEntryCount();
1142 for (int nX = 0; nX < nLen; ++nX)
1144 if (pEntries[nX] >= nPaletteEntryCount)
1146 SAL_WARN("vcl.gdi", "invalid colormap index: "
1147 << static_cast<unsigned int>(pEntries[nX]) << ", colormap len is: "
1148 << nPaletteEntryCount);
1149 pEntries[nX] = pEntries[nX] % nPaletteEntryCount;
1155 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1156 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1158 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd )
1160 // optimization for downscaling
1161 if( mnYpos & mnPreviewMask )
1162 return;
1163 if( nXStart & mnPreviewMask )
1164 return;
1166 // convert nY to pixel units in the target image
1167 // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1168 const sal_uInt32 nY = mnYpos >> mnPreviewShift;
1170 sal_uInt8* pTmp = mpInflateInBuf + 1;
1171 if ( mxAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1173 switch ( mxAcc->GetBitCount() )
1175 case 1 :
1177 if ( mbTransparent )
1179 for ( long nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1181 sal_uInt8 nCol;
1182 nShift = (nShift - 1) & 7;
1183 if ( nShift == 0 )
1184 nCol = *(pTmp++);
1185 else
1186 nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1187 nCol &= 1;
1189 ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1192 else
1193 { // ScanlineFormat::N1BitMsbPal
1194 for ( long nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1196 nShift = (nShift - 1) & 7;
1198 sal_uInt8 nCol;
1199 if ( nShift == 0 )
1200 nCol = *(pTmp++);
1201 else
1202 nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1203 nCol &= 1;
1205 ImplSetPixel( nY, nX, nCol );
1209 break;
1211 case 4 :
1213 if ( mbTransparent )
1215 if ( mnPngDepth == 4 ) // check if source has a two bit pixel format
1217 for ( long nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex )
1219 if( nXIndex & 1 )
1221 ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] );
1222 pTmp++;
1224 else
1226 ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] );
1230 else // if ( mnPngDepth == 2 )
1232 for ( long nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1234 sal_uInt8 nCol;
1235 switch( nXIndex & 3 )
1237 case 0 :
1238 nCol = *pTmp >> 6;
1239 break;
1241 case 1 :
1242 nCol = ( *pTmp >> 4 ) & 0x03 ;
1243 break;
1245 case 2 :
1246 nCol = ( *pTmp >> 2 ) & 0x03;
1247 break;
1249 case 3 :
1250 nCol = ( *pTmp++ ) & 0x03;
1251 break;
1253 default: // get rid of nCol uninitialized warning
1254 nCol = 0;
1255 break;
1258 ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1262 else
1264 if ( mnPngDepth == 4 ) // maybe the source is a two bitmap graphic
1265 { // ScanlineFormat::N4BitLsnPal
1266 for ( long nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1268 if( nXIndex & 1 )
1269 ImplSetPixel( nY, nX, *pTmp++ & 0x0f );
1270 else
1271 ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f );
1274 else // if ( mnPngDepth == 2 )
1276 for ( long nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1278 switch( nXIndex & 3 )
1280 case 0 :
1281 ImplSetPixel( nY, nX, *pTmp >> 6 );
1282 break;
1284 case 1 :
1285 ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 );
1286 break;
1288 case 2 :
1289 ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 );
1290 break;
1292 case 3 :
1293 ImplSetPixel( nY, nX, *pTmp++ & 0x03 );
1294 break;
1300 break;
1302 case 8 :
1304 if ( mbAlphaChannel )
1306 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1308 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1309 ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] );
1311 else
1313 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1314 ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] );
1317 else if ( mbTransparent )
1319 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1321 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ )
1322 ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1324 else
1326 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1327 ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1330 else // neither alpha nor transparency
1332 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1334 if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible
1336 int nLineBytes = maOrigSize.Width();
1337 if (mbPalette)
1338 SanitizePaletteIndexes(pTmp, nLineBytes, mxAcc);
1339 mxAcc->CopyScanline( nY, pTmp, ScanlineFormat::N8BitPal, nLineBytes );
1341 else
1343 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd )
1344 ImplSetPixel( nY, nX, *pTmp++ );
1347 else
1349 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1350 ImplSetPixel( nY, nX, *pTmp );
1354 break;
1356 default :
1357 mbStatus = false;
1358 break;
1361 else // no palette => truecolor
1363 // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
1364 static bool bCkeckDirectScanline(true);
1366 if( mbAlphaChannel )
1368 // has RGB + alpha
1369 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1371 // ScanlineFormat::N32BitTcRgba
1372 // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha
1373 const bool bDoDirectScanline(
1374 bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpMaskAcc);
1375 const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1377 if(bDoDirectScanline)
1379 // allocate scanlines on demand, reused for next line
1380 if(!mpScanline)
1382 #if OSL_DEBUG_LEVEL > 0
1383 mnAllocSizeScanline = maOrigSize.Width() * 3;
1384 #endif
1385 mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1388 if(!mpScanlineAlpha)
1390 #if OSL_DEBUG_LEVEL > 0
1391 mnAllocSizeScanlineAlpha = maOrigSize.Width();
1392 #endif
1393 mpScanlineAlpha = new sal_uInt8[maOrigSize.Width()];
1397 if(bDoDirectScanline)
1399 OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1400 OSL_ENSURE(mpScanlineAlpha, "No ScanlineAlpha allocated (!)");
1401 #if OSL_DEBUG_LEVEL > 0
1402 OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1403 OSL_ENSURE(mnAllocSizeScanlineAlpha >= maOrigSize.Width(), "Allocated ScanlineAlpha too small (!)");
1404 #endif
1405 sal_uInt8* pScanline(mpScanline);
1406 sal_uInt8* pScanlineAlpha(mpScanlineAlpha);
1408 for (long nX(0); nX < maOrigSize.Width(); nX++, pTmp += 4)
1410 // prepare content line as BGR by reordering when copying
1411 // do not forget to invert alpha (source is alpha, target is opacity)
1412 if(bCustomColorTable)
1414 *pScanline++ = mpColorTable[pTmp[2]];
1415 *pScanline++ = mpColorTable[pTmp[1]];
1416 *pScanline++ = mpColorTable[pTmp[0]];
1417 *pScanlineAlpha++ = ~pTmp[3];
1419 else
1421 *pScanline++ = pTmp[2];
1422 *pScanline++ = pTmp[1];
1423 *pScanline++ = pTmp[0];
1424 *pScanlineAlpha++ = ~pTmp[3];
1428 // copy scanlines directly to bitmaps for content and alpha; use the formats which
1429 // are able to copy directly to BitmapBuffer
1430 mxAcc->CopyScanline(nY, mpScanline, ScanlineFormat::N24BitTcBgr, maOrigSize.Width() * 3);
1431 mpMaskAcc->CopyScanline(nY, mpScanlineAlpha, ScanlineFormat::N8BitPal, maOrigSize.Width());
1433 else
1435 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1437 if(bCustomColorTable)
1439 ImplSetAlphaPixel(
1442 BitmapColor(
1443 mpColorTable[ pTmp[ 0 ] ],
1444 mpColorTable[ pTmp[ 1 ] ],
1445 mpColorTable[ pTmp[ 2 ] ]),
1446 pTmp[ 3 ]);
1448 else
1450 ImplSetAlphaPixel(
1453 BitmapColor(
1454 pTmp[0],
1455 pTmp[1],
1456 pTmp[2]),
1457 pTmp[3]);
1462 else
1464 // BMP_FORMAT_64BIT_TC_RGBA
1465 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 )
1467 ImplSetAlphaPixel(
1470 BitmapColor(
1471 mpColorTable[ pTmp[ 0 ] ],
1472 mpColorTable[ pTmp[ 2 ] ],
1473 mpColorTable[ pTmp[ 4 ] ]),
1474 pTmp[6]);
1478 else if( mbTransparent ) // has RGB + transparency
1480 // ScanlineFormat::N24BitTcRgb
1481 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1482 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1484 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1486 sal_uInt8 nRed = pTmp[ 0 ];
1487 sal_uInt8 nGreen = pTmp[ 1 ];
1488 sal_uInt8 nBlue = pTmp[ 2 ];
1489 bool bTransparent = ( ( nRed == mnTransRed )
1490 && ( nGreen == mnTransGreen )
1491 && ( nBlue == mnTransBlue ) );
1493 ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1494 mpColorTable[ nGreen ],
1495 mpColorTable[ nBlue ] ), bTransparent );
1498 else
1500 // BMP_FORMAT_48BIT_TC_RGB
1501 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1503 sal_uInt8 nRed = pTmp[ 0 ];
1504 sal_uInt8 nGreen = pTmp[ 2 ];
1505 sal_uInt8 nBlue = pTmp[ 4 ];
1506 bool bTransparent = ( ( nRed == mnTransRed )
1507 && ( nGreen == mnTransGreen )
1508 && ( nBlue == mnTransBlue ) );
1510 ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1511 mpColorTable[ nGreen ],
1512 mpColorTable[ nBlue ] ), bTransparent );
1516 else // has RGB but neither alpha nor transparency
1518 // ScanlineFormat::N24BitTcRgb
1519 // only use DirectScanline when we have no preview shifting stuff and access to content
1520 const bool bDoDirectScanline(
1521 bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift);
1522 const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1524 if(bDoDirectScanline && !mpScanline)
1526 // allocate scanlines on demand, reused for next line
1527 #if OSL_DEBUG_LEVEL > 0
1528 mnAllocSizeScanline = maOrigSize.Width() * 3;
1529 #endif
1530 mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1533 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1535 if(bDoDirectScanline)
1537 OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1538 #if OSL_DEBUG_LEVEL > 0
1539 OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1540 #endif
1541 sal_uInt8* pScanline(mpScanline);
1543 for (long nX(0); nX < maOrigSize.Width(); nX++, pTmp += 3)
1545 // prepare content line as BGR by reordering when copying
1546 if(bCustomColorTable)
1548 *pScanline++ = mpColorTable[pTmp[2]];
1549 *pScanline++ = mpColorTable[pTmp[1]];
1550 *pScanline++ = mpColorTable[pTmp[0]];
1552 else
1554 *pScanline++ = pTmp[2];
1555 *pScanline++ = pTmp[1];
1556 *pScanline++ = pTmp[0];
1560 // copy scanline directly to bitmap for content; use the format which is able to
1561 // copy directly to BitmapBuffer
1562 mxAcc->CopyScanline(nY, mpScanline, ScanlineFormat::N24BitTcBgr, maOrigSize.Width() * 3);
1564 else
1566 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1568 if(bCustomColorTable)
1570 ImplSetPixel(
1573 BitmapColor(
1574 mpColorTable[ pTmp[ 0 ] ],
1575 mpColorTable[ pTmp[ 1 ] ],
1576 mpColorTable[ pTmp[ 2 ] ]));
1578 else
1580 ImplSetPixel(
1583 BitmapColor(
1584 pTmp[0],
1585 pTmp[1],
1586 pTmp[2]));
1591 else
1593 // BMP_FORMAT_48BIT_TC_RGB
1594 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1595 for ( long nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1597 ImplSetPixel(
1600 BitmapColor(
1601 mpColorTable[ pTmp[ 0 ] ],
1602 mpColorTable[ pTmp[ 2 ] ],
1603 mpColorTable[ pTmp[ 4 ] ]));
1610 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor )
1612 // TODO: get preview mode checks out of inner loop
1613 if( nX & mnPreviewMask )
1614 return;
1615 nX >>= mnPreviewShift;
1617 mxAcc->SetPixel( nY, nX, rBitmapColor );
1620 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex )
1622 // TODO: get preview mode checks out of inner loop
1623 if( nX & mnPreviewMask )
1624 return;
1625 nX >>= mnPreviewShift;
1627 mxAcc->SetPixelIndex(nY, nX, SanitizePaletteIndex(nPalIndex, mxAcc->GetPaletteEntryCount()));
1630 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, bool bTrans )
1632 // TODO: get preview mode checks out of inner loop
1633 if( nX & mnPreviewMask )
1634 return;
1635 nX >>= mnPreviewShift;
1637 mxAcc->SetPixel( nY, nX, rBitmapColor );
1639 if ( bTrans )
1640 mpMaskAcc->SetPixel( nY, nX, mcTranspColor );
1641 else
1642 mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor );
1645 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1646 sal_uInt8 nPalIndex, sal_uInt8 nAlpha )
1648 // TODO: get preview mode checks out of inner loop
1649 if( nX & mnPreviewMask )
1650 return;
1651 nX >>= mnPreviewShift;
1653 mxAcc->SetPixelIndex(nY, nX, SanitizePaletteIndex(nPalIndex, mxAcc->GetPaletteEntryCount()));
1654 mpMaskAcc->SetPixel(nY, nX, BitmapColor(~nAlpha));
1657 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1658 const BitmapColor& rBitmapColor, sal_uInt8 nAlpha )
1660 // TODO: get preview mode checks out of inner loop
1661 if( nX & mnPreviewMask )
1662 return;
1663 nX >>= mnPreviewShift;
1665 mxAcc->SetPixel( nY, nX, rBitmapColor );
1666 if (!mpMaskAcc)
1667 return;
1668 mpMaskAcc->SetPixel(nY, nX, BitmapColor(~nAlpha));
1671 sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32()
1673 sal_uInt32 nRet;
1674 nRet = *maDataIter++;
1675 nRet <<= 8;
1676 nRet |= *maDataIter++;
1677 nRet <<= 8;
1678 nRet |= *maDataIter++;
1679 nRet <<= 8;
1680 nRet |= *maDataIter++;
1681 return nRet;
1684 PNGReader::PNGReader(SvStream& rIStream) :
1685 mpImpl(new vcl::PNGReaderImpl(rIStream))
1689 PNGReader::~PNGReader()
1693 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint )
1695 return mpImpl->GetBitmapEx( i_rPreviewSizeHint );
1698 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const
1700 return mpImpl->GetAllChunks();
1703 void PNGReader::SetIgnoreGammaChunk(bool bIgnoreGammaChunk)
1705 mpImpl->SetIgnoreGammaChunk(bIgnoreGammaChunk);
1708 } // namespace vcl
1710 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */