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