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