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