1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <vcl/pngread.hxx>
25 #include <rtl/alloc.h>
26 #include <tools/zcodec.hxx>
27 #include <tools/stream.hxx>
28 #include <vcl/bitmapaccess.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/alpha.hxx>
31 #include <osl/endian.h>
32 #include <o3tl/make_unique.hxx>
37 #define PNGCHUNK_IHDR 0x49484452
38 #define PNGCHUNK_PLTE 0x504c5445
39 #define PNGCHUNK_IDAT 0x49444154
40 #define PNGCHUNK_IEND 0x49454e44
41 #define PNGCHUNK_bKGD 0x624b4744
42 #define PNGCHUNK_gAMA 0x67414d41
43 #define PNGCHUNK_pHYs 0x70485973
44 #define PNGCHUNK_tRNS 0x74524e53
46 #define VIEWING_GAMMA 2.35
47 #define DISPLAY_GAMMA 1.0
50 static const sal_uInt8 mpDefaultColorTable
[ 256 ] =
51 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
52 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
53 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
54 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
55 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
58 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
59 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
60 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
62 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
63 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
64 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
65 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
66 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
72 SvStream
& mrPNGStream
;
73 SvStreamEndian mnOrigStreamMode
;
75 std::vector
<vcl::PNGReader::ChunkData
> maChunkSeq
;
76 std::vector
<vcl::PNGReader::ChunkData
>::iterator maChunkIter
;
77 std::vector
<sal_uInt8
>::iterator maDataIter
;
79 std::unique_ptr
<Bitmap
> mpBmp
;
80 Bitmap::ScopedWriteAccess mxAcc
;
81 std::unique_ptr
<Bitmap
> mpMaskBmp
;
82 Bitmap::ScopedWriteAccess mxMaskAcc
;
83 std::unique_ptr
<AlphaMask
> mpAlphaMask
;
84 AlphaMask::ScopedWriteAccess mxAlphaAcc
;
85 BitmapWriteAccess
* mpMaskAcc
;
88 sal_uInt8
* mpInflateInBuf
; // as big as the size of a scanline + alphachannel + 1
89 sal_uInt8
* mpScanPrior
; // pointer to the latest scanline
90 sal_uInt8
* mpTransTab
; // for transparency in images with palette colortype
91 sal_uInt8
* mpScanCurrent
; // pointer into the current scanline
92 sal_uInt8
* mpColorTable
;
93 std::size_t mnStreamSize
; // estimate of PNG file size
94 sal_uInt32 mnChunkType
; // Type of current PNG chunk
95 sal_Int32 mnChunkLen
; // Length of current PNG chunk
96 Size maOrigSize
; // pixel size of the full image
97 Size maTargetSize
; // pixel size of the result image
98 Size maPhysSize
; // preferred size in MapUnit::Map100thMM units
99 sal_uInt32 mnBPP
; // number of bytes per pixel
100 sal_uInt32 mnScansize
; // max size of scanline
101 sal_uInt32 mnYpos
; // latest y position in full image
102 int mnPass
; // if interlaced the latest pass ( 1..7 ) else 7
103 sal_uInt32 mnXStart
; // the starting X for the current pass
104 sal_uInt32 mnXAdd
; // the increment for input images X coords for the current pass
105 sal_uInt32 mnYAdd
; // the increment for input images Y coords for the current pass
106 int mnPreviewShift
; // shift to convert orig image coords into preview image coords
107 int mnPreviewMask
; // == ((1 << mnPreviewShift) - 1)
108 sal_uInt16 mnTargetDepth
; // pixel depth of target bitmap
109 sal_uInt8 mnTransRed
;
110 sal_uInt8 mnTransGreen
;
111 sal_uInt8 mnTransBlue
;
112 sal_uInt8 mnPngDepth
; // pixel depth of PNG data
113 sal_uInt8 mnColorType
;
114 sal_uInt8 mnCompressionType
;
115 sal_uInt8 mnFilterType
;
116 sal_uInt8 mnInterlaceType
;
117 BitmapColor mcTranspColor
; // transparency mask's transparency "color"
118 BitmapColor mcOpaqueColor
; // transparency mask's opaque "color"
119 bool mbTransparent
: 1; // graphic includes an tRNS Chunk or an alpha Channel
120 bool mbAlphaChannel
: 1; // is true for ColorType 4 and 6
121 bool mbRGBTriple
: 1;
122 bool mbPalette
: 1; // false if we need a Palette
123 bool mbGrayScale
: 1;
124 bool mbzCodecInUse
: 1;
126 bool mbIDAT
: 1; // true if finished with enough IDAT chunks
127 bool mbGamma
: 1; // true if Gamma Correction available
128 bool mbpHYs
: 1; // true if physical size of pixel available
129 bool mbIgnoreGammaChunk
: 1;
131 #if OSL_DEBUG_LEVEL > 0
132 // do some checks in debug mode
133 sal_Int32 mnAllocSizeScanline
;
134 sal_Int32 mnAllocSizeScanlineAlpha
;
136 // the temporary Scanline (and alpha) for direct scanline copy to Bitmap
137 sal_uInt8
* mpScanline
;
138 sal_uInt8
* mpScanlineAlpha
;
140 bool ReadNextChunk();
141 void ReadRemainingChunks();
143 void ImplSetPixel( sal_uInt32 y
, sal_uInt32 x
, const BitmapColor
& );
144 void ImplSetPixel( sal_uInt32 y
, sal_uInt32 x
, sal_uInt8 nPalIndex
);
145 void ImplSetTranspPixel( sal_uInt32 y
, sal_uInt32 x
, const BitmapColor
&, bool bTrans
);
146 void ImplSetAlphaPixel( sal_uInt32 y
, sal_uInt32 x
, sal_uInt8 nPalIndex
, sal_uInt8 nAlpha
);
147 void ImplSetAlphaPixel( sal_uInt32 y
, sal_uInt32 x
, const BitmapColor
&, sal_uInt8 nAlpha
);
149 bool ImplPreparePass();
150 void ImplApplyFilter();
151 void ImplDrawScanline( sal_uInt32 nXStart
, sal_uInt32 nXAdd
);
152 bool ImplReadTransparent();
154 void ImplGetBackground();
155 sal_uInt8
ImplScaleColor();
156 bool ImplReadHeader( const Size
& rPreviewSizeHint
);
157 bool ImplReadPalette();
158 void ImplGetGrayPalette( sal_uInt16
);
159 sal_uInt32
ImplReadsal_uInt32();
163 explicit PNGReaderImpl( SvStream
& );
166 BitmapEx
GetBitmapEx( const Size
& rPreviewSizeHint
);
167 const std::vector
<vcl::PNGReader::ChunkData
>& GetAllChunks();
168 void SetIgnoreGammaChunk( bool bIgnore
){ mbIgnoreGammaChunk
= bIgnore
; };
171 PNGReaderImpl::PNGReaderImpl( SvStream
& rPNGStream
)
172 : mrPNGStream( rPNGStream
),
173 mpMaskAcc ( nullptr ),
174 mpInflateInBuf ( nullptr ),
175 mpScanPrior ( nullptr ),
176 mpTransTab ( nullptr ),
177 mpScanCurrent ( nullptr ),
178 mpColorTable ( const_cast<sal_uInt8
*>(mpDefaultColorTable
) ),
194 mnCompressionType( 0 ),
196 mnInterlaceType ( 0 ),
197 mbTransparent( false ),
198 mbAlphaChannel( false ),
199 mbRGBTriple( false ),
201 mbGrayScale( false ),
202 mbzCodecInUse ( false ),
207 mbIgnoreGammaChunk ( false ),
208 #if OSL_DEBUG_LEVEL > 0
209 mnAllocSizeScanline(0),
210 mnAllocSizeScanlineAlpha(0),
213 mpScanlineAlpha(nullptr)
215 // prepare the PNG data stream
216 mnOrigStreamMode
= mrPNGStream
.GetEndian();
217 mrPNGStream
.SetEndian( SvStreamEndian::BIG
);
219 // prepare the chunk reader
220 maChunkSeq
.reserve( 16 );
221 maChunkIter
= maChunkSeq
.begin();
223 // estimate PNG file size (to allow sanity checks)
224 const std::size_t nStreamPos
= mrPNGStream
.Tell();
225 mrPNGStream
.Seek( STREAM_SEEK_TO_END
);
226 mnStreamSize
= mrPNGStream
.Tell();
227 mrPNGStream
.Seek( nStreamPos
);
229 // check the PNG header magic
230 sal_uInt32 nDummy
= 0;
231 mrPNGStream
.ReadUInt32( nDummy
);
232 mbStatus
= (nDummy
== 0x89504e47);
233 mrPNGStream
.ReadUInt32( nDummy
);
234 mbStatus
= (nDummy
== 0x0d0a1a0a) && mbStatus
;
237 mnPreviewMask
= (1 << mnPreviewShift
) - 1;
240 PNGReaderImpl::~PNGReaderImpl()
242 mrPNGStream
.SetEndian( mnOrigStreamMode
);
245 mpZCodec
.EndCompression();
247 if( mpColorTable
!= mpDefaultColorTable
)
248 delete[] mpColorTable
;
251 delete[] mpInflateInBuf
;
252 delete[] mpScanPrior
;
255 delete[] mpScanlineAlpha
;
258 bool PNGReaderImpl::ReadNextChunk()
260 if( maChunkIter
== maChunkSeq
.end() )
262 // get the next chunk from the stream
264 // unless we are at the end of the PNG stream
265 if( mrPNGStream
.IsEof() || (mrPNGStream
.GetError() != ERRCODE_NONE
) )
267 if( !maChunkSeq
.empty() && (maChunkSeq
.back().nType
== PNGCHUNK_IEND
) )
270 PNGReader::ChunkData aDummyChunk
;
271 maChunkIter
= maChunkSeq
.insert( maChunkSeq
.end(), aDummyChunk
);
272 PNGReader::ChunkData
& rChunkData
= *maChunkIter
;
274 // read the chunk header
275 mrPNGStream
.ReadInt32( mnChunkLen
).ReadUInt32( mnChunkType
);
276 rChunkData
.nType
= mnChunkType
;
278 // fdo#61847 truncate over-long, trailing chunks
279 const std::size_t nStreamPos
= mrPNGStream
.Tell();
280 if( mnChunkLen
< 0 || nStreamPos
+ mnChunkLen
>= mnStreamSize
)
281 mnChunkLen
= mnStreamSize
- nStreamPos
;
283 // calculate chunktype CRC (swap it back to original byte order)
284 sal_uInt32 nChunkType
= mnChunkType
;
285 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
286 nChunkType
= OSL_SWAPDWORD( nChunkType
);
288 sal_uInt32 nCRC32
= rtl_crc32( 0, &nChunkType
, 4 );
290 // read the chunk data and check the CRC
291 if( mnChunkLen
&& !mrPNGStream
.IsEof() )
293 rChunkData
.aData
.resize( mnChunkLen
);
295 sal_Int32 nBytesRead
= 0;
297 sal_uInt8
* pPtr
= &rChunkData
.aData
[ nBytesRead
];
298 nBytesRead
+= mrPNGStream
.ReadBytes(pPtr
, mnChunkLen
- nBytesRead
);
299 } while ( ( nBytesRead
< mnChunkLen
) && ( mrPNGStream
.GetError() == ERRCODE_NONE
) );
301 nCRC32
= rtl_crc32( nCRC32
, &rChunkData
.aData
[ 0 ], mnChunkLen
);
302 maDataIter
= rChunkData
.aData
.begin();
304 sal_uInt32
nCheck(0);
305 mrPNGStream
.ReadUInt32( nCheck
);
306 if( nCRC32
!= nCheck
)
311 // the next chunk was already read
312 mnChunkType
= (*maChunkIter
).nType
;
313 mnChunkLen
= (*maChunkIter
).aData
.size();
314 maDataIter
= (*maChunkIter
).aData
.begin();
318 return mnChunkType
!= PNGCHUNK_IEND
;
321 // read the remaining chunks from mrPNGStream
322 void PNGReaderImpl::ReadRemainingChunks()
324 while( ReadNextChunk() ) ;
327 const std::vector
< vcl::PNGReader::ChunkData
>& PNGReaderImpl::GetAllChunks()
329 ReadRemainingChunks();
333 BitmapEx
PNGReaderImpl::GetBitmapEx( const Size
& rPreviewSizeHint
)
335 // reset to the first chunk
336 maChunkIter
= maChunkSeq
.begin();
338 // first chunk must be IDHR
339 if( mbStatus
&& ReadNextChunk() )
341 if (mnChunkType
== PNGCHUNK_IHDR
)
342 mbStatus
= ImplReadHeader( rPreviewSizeHint
);
347 // parse the remaining chunks
348 while (mbStatus
&& !mbIDAT
&& ReadNextChunk())
350 switch( mnChunkType
)
354 mbStatus
= false; //IHDR should only appear as the first chunk
358 case PNGCHUNK_gAMA
: // the gamma chunk must precede
359 { // the 'IDAT' and also the 'PLTE'(if available )
360 if ( !mbIgnoreGammaChunk
&& !mbIDAT
)
368 mbStatus
= ImplReadPalette();
374 if ( !mbIDAT
) // the tRNS chunk must precede the IDAT
375 mbStatus
= ImplReadTransparent();
379 case PNGCHUNK_bKGD
: // the background chunk must appear
381 if ( !mbIDAT
&& mbPalette
) // before the 'IDAT' and after the
382 ImplGetBackground(); // PLTE(if available ) chunk.
388 if ( !mpInflateInBuf
) // taking care that the header has properly been read
390 else if ( !mbIDAT
) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes
397 if ( !mbIDAT
&& mnChunkLen
== 9 )
399 sal_uInt32 nXPixelPerMeter
= ImplReadsal_uInt32();
400 sal_uInt32 nYPixelPerMeter
= ImplReadsal_uInt32();
402 sal_uInt8 nUnitSpecifier
= *maDataIter
++;
403 if( (nUnitSpecifier
== 1) && nXPixelPerMeter
&& nYPixelPerMeter
)
407 // convert into MapUnit::Map100thMM
408 maPhysSize
.Width() = (sal_Int32
)( (100000.0 * maOrigSize
.Width()) / nXPixelPerMeter
);
409 maPhysSize
.Height() = (sal_Int32
)( (100000.0 * maOrigSize
.Height()) / nYPixelPerMeter
);
416 mbStatus
= mbIDAT
; // there is a problem if the image is not complete yet
421 // release write access of the bitmaps
427 // return the resulting BitmapEx
430 if( !mbStatus
|| !mbIDAT
)
435 aRet
= BitmapEx( *mpBmp
, *mpAlphaMask
);
436 else if ( mpMaskBmp
)
437 aRet
= BitmapEx( *mpBmp
, *mpMaskBmp
);
441 if ( mbpHYs
&& maPhysSize
.Width() && maPhysSize
.Height() )
443 aRet
.SetPrefMapMode( MapUnit::Map100thMM
);
444 aRet
.SetPrefSize( maPhysSize
);
450 bool PNGReaderImpl::ImplReadHeader( const Size
& rPreviewSizeHint
)
452 if( mnChunkLen
< 13 )
455 maOrigSize
.Width() = ImplReadsal_uInt32();
456 maOrigSize
.Height() = ImplReadsal_uInt32();
458 if (maOrigSize
.Width() <= 0 || maOrigSize
.Height() <= 0)
461 mnPngDepth
= *(maDataIter
++);
462 mnColorType
= *(maDataIter
++);
464 mnCompressionType
= *(maDataIter
++);
465 if( mnCompressionType
!= 0 ) // unknown compression type
468 mnFilterType
= *(maDataIter
++);
469 if( mnFilterType
!= 0 ) // unknown filter type
472 mnInterlaceType
= *(maDataIter
++);
473 switch ( mnInterlaceType
) // filter type valid ?
475 case 0 : // progressive image
478 case 1 : // Adam7-interlaced image
486 mbIDAT
= mbAlphaChannel
= mbTransparent
= false;
487 mbGrayScale
= mbRGBTriple
= false;
488 mnTargetDepth
= mnPngDepth
;
489 sal_uInt64 nScansize64
= ( ( static_cast< sal_uInt64
>( maOrigSize
.Width() ) * mnPngDepth
) + 7 ) >> 3;
491 // valid color types are 0,2,3,4 & 6
492 switch ( mnColorType
)
494 case 0 : // each pixel is a grayscale
496 switch ( mnPngDepth
)
498 case 2 : // 2bit target not available -> use four bits
499 mnTargetDepth
= 4; // we have to expand the bitmap
503 mnTargetDepth
= 8; // we have to reduce the bitmap
516 case 2 : // each pixel is an RGB triple
520 switch ( mnPngDepth
)
522 case 16 : // we have to reduce the bitmap
532 case 3 : // each pixel is a palette index
534 switch ( mnPngDepth
)
537 mnTargetDepth
= 4; // we have to expand the bitmap
550 case 4 : // each pixel is a grayscale sample followed by an alpha sample
553 mbAlphaChannel
= true;
554 switch ( mnPngDepth
)
557 mnTargetDepth
= 8; // we have to reduce the bitmap
568 case 6 : // each pixel is an RGB triple followed by an alpha sample
572 mbAlphaChannel
= true;
575 case 16 : // we have to reduce the bitmap
589 mnBPP
= static_cast< sal_uInt32
>( nScansize64
/ maOrigSize
.Width() );
593 nScansize64
++; // each scanline includes one filterbyte
595 if ( nScansize64
> SAL_MAX_UINT32
)
598 // assume max theoretical compression of 1:1032
599 sal_uInt64 nMinSizeRequired
= (nScansize64
* maOrigSize
.Height()) / 1032;
600 if (nMinSizeRequired
> mnStreamSize
)
602 SAL_WARN("vcl.gdi", "overlarge png dimensions: " <<
603 maOrigSize
.Width() << " x " << maOrigSize
.Height() << " depth: " << (int)mnPngDepth
<<
604 " couldn't be supplied by file length " << mnStreamSize
<< " at least " << nMinSizeRequired
<< " needed ");
608 mnScansize
= static_cast< sal_uInt32
>( nScansize64
);
610 // calculate target size from original size and the preview hint
611 if( rPreviewSizeHint
.Width() || rPreviewSizeHint
.Height() )
613 Size
aPreviewSize( rPreviewSizeHint
.Width(), rPreviewSizeHint
.Height() );
614 maTargetSize
= maOrigSize
;
616 if( aPreviewSize
.Width() == 0 ) {
617 aPreviewSize
.setWidth( ( maOrigSize
.Width()*aPreviewSize
.Height() )/maOrigSize
.Height() );
618 if( aPreviewSize
.Width() <= 0 )
619 aPreviewSize
.setWidth( 1 );
620 } else if( aPreviewSize
.Height() == 0 ) {
621 aPreviewSize
.setHeight( ( maOrigSize
.Height()*aPreviewSize
.Width() )/maOrigSize
.Width() );
622 if( aPreviewSize
.Height() <= 0 )
623 aPreviewSize
.setHeight( 1 );
626 if( aPreviewSize
.Width() < maOrigSize
.Width() && aPreviewSize
.Height() < maOrigSize
.Height() ) {
627 SAL_INFO( "vcl.gdi", "preview size " << aPreviewSize
.Width() << " " << aPreviewSize
.Height() );
629 for( int i
= 1; i
< 5; ++i
)
631 if( (maTargetSize
.Width() >> i
) < aPreviewSize
.Width() )
633 if( (maTargetSize
.Height() >> i
) < aPreviewSize
.Height() )
638 mnPreviewMask
= (1 << mnPreviewShift
) - 1;
642 maTargetSize
.Width() = (maOrigSize
.Width() + mnPreviewMask
) >> mnPreviewShift
;
643 maTargetSize
.Height() = (maOrigSize
.Height() + mnPreviewMask
) >> mnPreviewShift
;
645 //round bits up to nearest multiple of 8 and divide by 8 to get num of bytes per pixel
646 int nBytesPerPixel
= ((mnTargetDepth
+ 7) & ~7)/8;
648 //stupidly big, forget about it
649 if (maTargetSize
.Width() >= SAL_MAX_INT32
/ nBytesPerPixel
/ maTargetSize
.Height())
651 SAL_WARN( "vcl.gdi", "overlarge png dimensions: " <<
652 maTargetSize
.Width() << " x " << maTargetSize
.Height() << " depth: " << mnTargetDepth
);
656 // TODO: switch between both scanlines instead of copying
657 mpInflateInBuf
= new (std::nothrow
) sal_uInt8
[ mnScansize
];
658 mpScanCurrent
= mpInflateInBuf
;
659 mpScanPrior
= new (std::nothrow
) sal_uInt8
[ mnScansize
];
661 if ( !mpInflateInBuf
|| !mpScanPrior
)
664 mpBmp
= o3tl::make_unique
<Bitmap
>( maTargetSize
, mnTargetDepth
);
665 mxAcc
= Bitmap::ScopedWriteAccess(*mpBmp
);
669 if ( mbAlphaChannel
)
671 mpAlphaMask
= o3tl::make_unique
<AlphaMask
>( maTargetSize
);
672 mpAlphaMask
->Erase( 128 );
673 mxAlphaAcc
= AlphaMask::ScopedWriteAccess(*mpAlphaMask
);
674 mpMaskAcc
= mxAlphaAcc
.get();
680 ImplGetGrayPalette( mnPngDepth
);
687 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth
)
692 sal_uInt16 nPaletteEntryCount
= 1 << nBitDepth
;
693 sal_uInt32 nAdd
= nBitDepth
? 256 / (nPaletteEntryCount
- 1) : 0;
695 // no bitdepth==2 available
696 // but bitdepth==4 with two unused bits is close enough
698 nPaletteEntryCount
= 16;
700 mxAcc
->SetPaletteEntryCount( nPaletteEntryCount
);
701 for ( sal_uInt32 i
= 0, nStart
= 0; nStart
< 256; i
++, nStart
+= nAdd
)
702 mxAcc
->SetPaletteColor( (sal_uInt16
)i
, BitmapColor( mpColorTable
[ nStart
],
703 mpColorTable
[ nStart
], mpColorTable
[ nStart
] ) );
706 bool PNGReaderImpl::ImplReadPalette()
708 sal_uInt16 nCount
= static_cast<sal_uInt16
>( mnChunkLen
/ 3 );
710 if ( ( ( mnChunkLen
% 3 ) == 0 ) && ( ( 0 < nCount
) && ( nCount
<= 256 ) ) && mxAcc
)
713 mxAcc
->SetPaletteEntryCount( nCount
);
715 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
717 sal_uInt8 nRed
= mpColorTable
[ *maDataIter
++ ];
718 sal_uInt8 nGreen
= mpColorTable
[ *maDataIter
++ ];
719 sal_uInt8 nBlue
= mpColorTable
[ *maDataIter
++ ];
720 mxAcc
->SetPaletteColor( i
, Color( nRed
, nGreen
, nBlue
) );
729 bool PNGReaderImpl::ImplReadTransparent()
731 bool bNeedAlpha
= false;
733 if ( mpTransTab
== nullptr )
735 switch ( mnColorType
)
739 if ( mnChunkLen
== 2 )
741 mpTransTab
= new sal_uInt8
[ 256 ];
742 memset( mpTransTab
, 0xff, 256);
743 // color type 0 and 4 is always greyscale,
744 // so the return value can be used as index
745 sal_uInt8 nIndex
= ImplScaleColor();
746 mpTransTab
[ nIndex
] = 0;
747 mbTransparent
= true;
754 if ( mnChunkLen
== 6 )
756 mnTransRed
= ImplScaleColor();
757 mnTransGreen
= ImplScaleColor();
758 mnTransBlue
= ImplScaleColor();
759 mbTransparent
= true;
766 if ( mnChunkLen
<= 256 )
768 mbTransparent
= true;
769 mpTransTab
= new sal_uInt8
[ 256 ];
770 memset( mpTransTab
, 0xff, 256 );
773 memcpy( mpTransTab
, &(*maDataIter
), mnChunkLen
);
774 maDataIter
+= mnChunkLen
;
775 // need alpha transparency if not on/off masking
776 for( int i
= 0; i
< mnChunkLen
; ++i
)
777 bNeedAlpha
|= (mpTransTab
[i
]!=0x00) && (mpTransTab
[i
]!=0xFF);
785 if( mbTransparent
&& !mbAlphaChannel
&& !mpMaskBmp
)
789 mpAlphaMask
= o3tl::make_unique
<AlphaMask
>( maTargetSize
);
790 mxAlphaAcc
= AlphaMask::ScopedWriteAccess(*mpAlphaMask
);
791 mpMaskAcc
= mxAlphaAcc
.get();
795 mpMaskBmp
= o3tl::make_unique
<Bitmap
>( maTargetSize
, 1 );
796 mxMaskAcc
= Bitmap::ScopedWriteAccess(*mpMaskBmp
);
797 mpMaskAcc
= mxMaskAcc
.get();
799 mbTransparent
= (mpMaskAcc
!= nullptr);
802 mcOpaqueColor
= BitmapColor( 0x00 );
803 mcTranspColor
= BitmapColor( 0xFF );
804 mpMaskAcc
->Erase( 0x00 );
810 void PNGReaderImpl::ImplGetGamma()
815 sal_uInt32 nGammaValue
= ImplReadsal_uInt32();
816 double fGamma
= ( ( VIEWING_GAMMA
/ DISPLAY_GAMMA
) * ( (double)nGammaValue
/ 100000 ) );
817 double fInvGamma
= ( fGamma
<= 0.0 || fGamma
> 10.0 ) ? 1.0 : ( 1.0 / fGamma
);
819 if ( fInvGamma
!= 1.0 )
823 if ( mpColorTable
== mpDefaultColorTable
)
824 mpColorTable
= new sal_uInt8
[ 256 ];
826 for ( sal_Int32 i
= 0; i
< 256; i
++ )
827 mpColorTable
[ i
] = (sal_uInt8
)(pow((double)i
/255.0, fInvGamma
) * 255.0 + 0.5);
830 ImplGetGrayPalette( mnPngDepth
);
834 void PNGReaderImpl::ImplGetBackground()
836 switch ( mnColorType
)
840 if ( mnChunkLen
== 1 )
842 sal_uInt16 nCol
= *maDataIter
++;
843 if ( nCol
< mxAcc
->GetPaletteEntryCount() )
845 mxAcc
->Erase( mxAcc
->GetPaletteColor( (sal_uInt8
)nCol
) );
855 if ( mnChunkLen
== 2 )
857 // the color type 0 and 4 is always greyscale,
858 // so the return value can be used as index
859 sal_uInt8 nIndex
= ImplScaleColor();
860 mxAcc
->Erase( mxAcc
->GetPaletteColor( nIndex
) );
868 if ( mnChunkLen
== 6 )
870 sal_uInt8 nRed
= ImplScaleColor();
871 sal_uInt8 nGreen
= ImplScaleColor();
872 sal_uInt8 nBlue
= ImplScaleColor();
873 mxAcc
->Erase( Color( nRed
, nGreen
, nBlue
) );
880 // for color type 0 and 4 (greyscale) the return value is always index to the color
881 // 2 and 6 (RGB) the return value is always the 8 bit color component
882 sal_uInt8
PNGReaderImpl::ImplScaleColor()
884 sal_uInt32 nMask
= ( ( 1 << mnPngDepth
) - 1 );
885 sal_uInt16 nCol
= ( *maDataIter
++ << 8 );
887 nCol
+= *maDataIter
++ & (sal_uInt16
)nMask
;
889 if ( mnPngDepth
> 8 ) // convert 16bit graphics to 8
892 return (sal_uInt8
) nCol
;
895 // ImplReadIDAT reads as much image data as needed
897 void PNGReaderImpl::ImplReadIDAT()
901 if ( !mbzCodecInUse
)
903 mbzCodecInUse
= true;
904 mpZCodec
.BeginCompression( ZCODEC_NO_COMPRESSION
, true );
906 mpZCodec
.SetBreak( mnChunkLen
);
907 SvMemoryStream
aIStrm( &(*maDataIter
), mnChunkLen
, StreamMode::READ
);
909 while ( ( mpZCodec
.GetBreak() ) )
911 // get bytes needed to fill the current scanline
912 sal_Int32 nToRead
= mnScansize
- (mpScanCurrent
- mpInflateInBuf
);
913 sal_Int32 nRead
= mpZCodec
.ReadAsynchron( aIStrm
, mpScanCurrent
, nToRead
);
919 if ( nRead
< nToRead
)
921 mpScanCurrent
+= nRead
; // more ZStream data in the next IDAT chunk
924 else // this scanline is Finished
926 mpScanCurrent
= mpInflateInBuf
;
929 ImplDrawScanline( mnXStart
, mnXAdd
);
933 if ( mnYpos
>= (sal_uInt32
)maOrigSize
.Height() )
935 if( (mnPass
< 7) && mnInterlaceType
)
936 if( ImplPreparePass() )
946 mpZCodec
.EndCompression();
947 mbzCodecInUse
= false;
951 bool PNGReaderImpl::ImplPreparePass()
953 struct InterlaceParams
{ int mnXStart
, mnYStart
, mnXAdd
, mnYAdd
; };
954 static const InterlaceParams aInterlaceParams
[8] =
959 { 0, 0, 8, 8 }, // pass 1
960 { 4, 0, 8, 8 }, // pass 2
961 { 0, 4, 4, 8 }, // pass 3
962 { 2, 0, 4, 4 }, // pass 4
963 { 0, 2, 2, 4 }, // pass 5
964 { 1, 0, 2, 2 }, // pass 6
965 { 0, 1, 1, 2 } // pass 7
968 const InterlaceParams
* pParam
= &aInterlaceParams
[ 0 ];
969 if( mnInterlaceType
)
971 while( ++mnPass
<= 7 )
973 pParam
= &aInterlaceParams
[ mnPass
];
975 // skip this pass if the original image is too small for it
976 if( (pParam
->mnXStart
< maOrigSize
.Width())
977 && (pParam
->mnYStart
< maOrigSize
.Height()) )
983 // skip the last passes if possible (for scaled down target images)
984 if( mnPreviewMask
& (pParam
->mnXStart
| pParam
->mnYStart
) )
988 mnYpos
= pParam
->mnYStart
;
989 mnXStart
= pParam
->mnXStart
;
990 mnXAdd
= pParam
->mnXAdd
;
991 mnYAdd
= pParam
->mnYAdd
;
993 // in Interlace mode the size of scanline is not constant
994 // so first we calculate the number of entrys
995 long nScanWidth
= (maOrigSize
.Width() - mnXStart
+ mnXAdd
- 1) / mnXAdd
;
996 mnScansize
= nScanWidth
;
999 mnScansize
= 3 * nScanWidth
;
1001 if( mbAlphaChannel
)
1002 mnScansize
+= nScanWidth
;
1004 // convert to width in bytes
1005 mnScansize
= ( mnScansize
*mnPngDepth
+ 7 ) >> 3;
1007 ++mnScansize
; // scan size also needs room for the filtertype byte
1008 memset( mpScanPrior
, 0, mnScansize
);
1013 // ImplApplyFilter writes the complete Scanline (nY)
1014 // in interlace mode the parameter nXStart and nXAdd are non-zero
1016 void PNGReaderImpl::ImplApplyFilter()
1018 OSL_ASSERT( mnScansize
>= mnBPP
+ 1 );
1019 const sal_uInt8
* const pScanEnd
= mpInflateInBuf
+ mnScansize
;
1021 sal_uInt8 nFilterType
= *mpInflateInBuf
; // the filter type may change each scanline
1022 switch ( nFilterType
)
1024 default: // unknown Scanline Filter Type
1025 case 0: // Filter Type "None"
1026 // we let the pixels pass and display the data unfiltered
1029 case 1: // Scanline Filter Type "Sub"
1031 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1032 const sal_uInt8
* p2
= p1
;
1036 while (p1
< pScanEnd
)
1038 *p1
= static_cast<sal_uInt8
>( *p1
+ *(p2
++) );
1044 case 2: // Scanline Filter Type "Up"
1046 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1047 const sal_uInt8
* p2
= mpScanPrior
+ 1;
1049 // use pixels from prior line
1050 while( p1
< pScanEnd
)
1052 *p1
= static_cast<sal_uInt8
>( *p1
+ *(p2
++) );
1058 case 3: // Scanline Filter Type "Average"
1060 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1061 const sal_uInt8
* p2
= mpScanPrior
+ 1;
1062 const sal_uInt8
* p3
= p1
;
1064 // use one pixel from prior line
1065 for( int n
= mnBPP
; --n
>= 0; ++p1
, ++p2
)
1066 *p1
= static_cast<sal_uInt8
>( *p1
+ (*p2
>> 1) );
1068 // predict by averaging the left and prior line pixels
1069 while( p1
< pScanEnd
)
1071 *p1
= static_cast<sal_uInt8
>( *p1
+ ((*(p2
++) + *(p3
++)) >> 1) );
1077 case 4: // Scanline Filter Type "PathPredictor"
1079 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1080 const sal_uInt8
* p2
= mpScanPrior
+ 1;
1081 const sal_uInt8
* p3
= p1
;
1082 const sal_uInt8
* p4
= p2
;
1084 // use one pixel from prior line
1085 for( int n
= mnBPP
; --n
>= 0; ++p1
)
1086 *p1
= static_cast<sal_uInt8
>( *p1
+ *(p2
++) );
1088 // predict by using the left and the prior line pixels
1089 while( p1
< pScanEnd
)
1097 int npc
= npa
+ npb
;
1114 *p1
= static_cast<sal_uInt8
>( *p1
+ na
);
1121 memcpy( mpScanPrior
, mpInflateInBuf
, mnScansize
);
1126 sal_uInt8
SanitizePaletteIndex(sal_uInt8 nIndex
, sal_uInt16 nPaletteEntryCount
)
1128 if (nIndex
>= nPaletteEntryCount
)
1130 auto nSanitizedIndex
= nIndex
% nPaletteEntryCount
;
1131 SAL_WARN_IF(nIndex
!= nSanitizedIndex
, "vcl", "invalid colormap index: "
1132 << static_cast<unsigned int>(nIndex
) << ", colormap len is: "
1133 << nPaletteEntryCount
);
1134 nIndex
= nSanitizedIndex
;
1139 void SanitizePaletteIndexes(sal_uInt8
* pEntries
, int nLen
, const Bitmap::ScopedWriteAccess
& rAcc
)
1141 sal_uInt16 nPaletteEntryCount
= rAcc
->GetPaletteEntryCount();
1142 for (int nX
= 0; nX
< nLen
; ++nX
)
1144 if (pEntries
[nX
] >= nPaletteEntryCount
)
1146 SAL_WARN("vcl.gdi", "invalid colormap index: "
1147 << static_cast<unsigned int>(pEntries
[nX
]) << ", colormap len is: "
1148 << nPaletteEntryCount
);
1149 pEntries
[nX
] = pEntries
[nX
] % nPaletteEntryCount
;
1155 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1156 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1158 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart
, sal_uInt32 nXAdd
)
1160 // optimization for downscaling
1161 if( mnYpos
& mnPreviewMask
)
1163 if( nXStart
& mnPreviewMask
)
1166 // convert nY to pixel units in the target image
1167 // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1168 const sal_uInt32 nY
= mnYpos
>> mnPreviewShift
;
1170 sal_uInt8
* pTmp
= mpInflateInBuf
+ 1;
1171 if ( mxAcc
->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1173 switch ( mxAcc
->GetBitCount() )
1177 if ( mbTransparent
)
1179 for ( long nX
= nXStart
, nShift
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
)
1182 nShift
= (nShift
- 1) & 7;
1186 nCol
= static_cast<sal_uInt8
>( *pTmp
>> nShift
);
1189 ImplSetAlphaPixel( nY
, nX
, nCol
, mpTransTab
[ nCol
] );
1193 { // ScanlineFormat::N1BitMsbPal
1194 for ( long nX
= nXStart
, nShift
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
)
1196 nShift
= (nShift
- 1) & 7;
1202 nCol
= static_cast<sal_uInt8
>( *pTmp
>> nShift
);
1205 ImplSetPixel( nY
, nX
, nCol
);
1213 if ( mbTransparent
)
1215 if ( mnPngDepth
== 4 ) // check if source has a two bit pixel format
1217 for ( long nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, ++nXIndex
)
1221 ImplSetAlphaPixel( nY
, nX
, *pTmp
& 0x0f, mpTransTab
[ *pTmp
& 0x0f ] );
1226 ImplSetAlphaPixel( nY
, nX
, ( *pTmp
>> 4 ) & 0x0f, mpTransTab
[ *pTmp
>> 4 ] );
1230 else // if ( mnPngDepth == 2 )
1232 for ( long nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, nXIndex
++ )
1235 switch( nXIndex
& 3 )
1242 nCol
= ( *pTmp
>> 4 ) & 0x03 ;
1246 nCol
= ( *pTmp
>> 2 ) & 0x03;
1250 nCol
= ( *pTmp
++ ) & 0x03;
1253 default: // get rid of nCol uninitialized warning
1258 ImplSetAlphaPixel( nY
, nX
, nCol
, mpTransTab
[ nCol
] );
1264 if ( mnPngDepth
== 4 ) // maybe the source is a two bitmap graphic
1265 { // ScanlineFormat::N4BitLsnPal
1266 for ( long nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, nXIndex
++ )
1269 ImplSetPixel( nY
, nX
, *pTmp
++ & 0x0f );
1271 ImplSetPixel( nY
, nX
, ( *pTmp
>> 4 ) & 0x0f );
1274 else // if ( mnPngDepth == 2 )
1276 for ( long nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, nXIndex
++ )
1278 switch( nXIndex
& 3 )
1281 ImplSetPixel( nY
, nX
, *pTmp
>> 6 );
1285 ImplSetPixel( nY
, nX
, ( *pTmp
>> 4 ) & 0x03 );
1289 ImplSetPixel( nY
, nX
, ( *pTmp
>> 2 ) & 0x03 );
1293 ImplSetPixel( nY
, nX
, *pTmp
++ & 0x03 );
1304 if ( mbAlphaChannel
)
1306 if ( mnPngDepth
== 8 ) // maybe the source is a 16 bit grayscale
1308 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 2 )
1309 ImplSetAlphaPixel( nY
, nX
, pTmp
[ 0 ], pTmp
[ 1 ] );
1313 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 4 )
1314 ImplSetAlphaPixel( nY
, nX
, pTmp
[ 0 ], pTmp
[ 2 ] );
1317 else if ( mbTransparent
)
1319 if ( mnPngDepth
== 8 ) // maybe the source is a 16 bit grayscale
1321 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
++ )
1322 ImplSetAlphaPixel( nY
, nX
, *pTmp
, mpTransTab
[ *pTmp
] );
1326 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 2 )
1327 ImplSetAlphaPixel( nY
, nX
, *pTmp
, mpTransTab
[ *pTmp
] );
1330 else // neither alpha nor transparency
1332 if ( mnPngDepth
== 8 ) // maybe the source is a 16 bit grayscale
1334 if( nXAdd
== 1 && mnPreviewShift
== 0 ) // copy raw line data if possible
1336 int nLineBytes
= maOrigSize
.Width();
1338 SanitizePaletteIndexes(pTmp
, nLineBytes
, mxAcc
);
1339 mxAcc
->CopyScanline( nY
, pTmp
, ScanlineFormat::N8BitPal
, nLineBytes
);
1343 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
)
1344 ImplSetPixel( nY
, nX
, *pTmp
++ );
1349 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 2 )
1350 ImplSetPixel( nY
, nX
, *pTmp
);
1361 else // no palette => truecolor
1363 // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
1364 static bool bCkeckDirectScanline(true);
1366 if( mbAlphaChannel
)
1369 if ( mnPngDepth
== 8 ) // maybe the source has 16 bit per sample
1371 // ScanlineFormat::N32BitTcRgba
1372 // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha
1373 const bool bDoDirectScanline(
1374 bCkeckDirectScanline
&& !nXStart
&& 1 == nXAdd
&& !mnPreviewShift
&& mpMaskAcc
);
1375 const bool bCustomColorTable(mpColorTable
!= mpDefaultColorTable
);
1377 if(bDoDirectScanline
)
1379 // allocate scanlines on demand, reused for next line
1382 #if OSL_DEBUG_LEVEL > 0
1383 mnAllocSizeScanline
= maOrigSize
.Width() * 3;
1385 mpScanline
= new sal_uInt8
[maOrigSize
.Width() * 3];
1388 if(!mpScanlineAlpha
)
1390 #if OSL_DEBUG_LEVEL > 0
1391 mnAllocSizeScanlineAlpha
= maOrigSize
.Width();
1393 mpScanlineAlpha
= new sal_uInt8
[maOrigSize
.Width()];
1397 if(bDoDirectScanline
)
1399 OSL_ENSURE(mpScanline
, "No Scanline allocated (!)");
1400 OSL_ENSURE(mpScanlineAlpha
, "No ScanlineAlpha allocated (!)");
1401 #if OSL_DEBUG_LEVEL > 0
1402 OSL_ENSURE(mnAllocSizeScanline
>= maOrigSize
.Width() * 3, "Allocated Scanline too small (!)");
1403 OSL_ENSURE(mnAllocSizeScanlineAlpha
>= maOrigSize
.Width(), "Allocated ScanlineAlpha too small (!)");
1405 sal_uInt8
* pScanline(mpScanline
);
1406 sal_uInt8
* pScanlineAlpha(mpScanlineAlpha
);
1408 for (long nX(0); nX
< maOrigSize
.Width(); nX
++, pTmp
+= 4)
1410 // prepare content line as BGR by reordering when copying
1411 // do not forget to invert alpha (source is alpha, target is opacity)
1412 if(bCustomColorTable
)
1414 *pScanline
++ = mpColorTable
[pTmp
[2]];
1415 *pScanline
++ = mpColorTable
[pTmp
[1]];
1416 *pScanline
++ = mpColorTable
[pTmp
[0]];
1417 *pScanlineAlpha
++ = ~pTmp
[3];
1421 *pScanline
++ = pTmp
[2];
1422 *pScanline
++ = pTmp
[1];
1423 *pScanline
++ = pTmp
[0];
1424 *pScanlineAlpha
++ = ~pTmp
[3];
1428 // copy scanlines directly to bitmaps for content and alpha; use the formats which
1429 // are able to copy directly to BitmapBuffer
1430 mxAcc
->CopyScanline(nY
, mpScanline
, ScanlineFormat::N24BitTcBgr
, maOrigSize
.Width() * 3);
1431 mpMaskAcc
->CopyScanline(nY
, mpScanlineAlpha
, ScanlineFormat::N8BitPal
, maOrigSize
.Width());
1435 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 4 )
1437 if(bCustomColorTable
)
1443 mpColorTable
[ pTmp
[ 0 ] ],
1444 mpColorTable
[ pTmp
[ 1 ] ],
1445 mpColorTable
[ pTmp
[ 2 ] ]),
1464 // BMP_FORMAT_64BIT_TC_RGBA
1465 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 8 )
1471 mpColorTable
[ pTmp
[ 0 ] ],
1472 mpColorTable
[ pTmp
[ 2 ] ],
1473 mpColorTable
[ pTmp
[ 4 ] ]),
1478 else if( mbTransparent
) // has RGB + transparency
1480 // ScanlineFormat::N24BitTcRgb
1481 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1482 if ( mnPngDepth
== 8 ) // maybe the source has 16 bit per sample
1484 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 3 )
1486 sal_uInt8 nRed
= pTmp
[ 0 ];
1487 sal_uInt8 nGreen
= pTmp
[ 1 ];
1488 sal_uInt8 nBlue
= pTmp
[ 2 ];
1489 bool bTransparent
= ( ( nRed
== mnTransRed
)
1490 && ( nGreen
== mnTransGreen
)
1491 && ( nBlue
== mnTransBlue
) );
1493 ImplSetTranspPixel( nY
, nX
, BitmapColor( mpColorTable
[ nRed
],
1494 mpColorTable
[ nGreen
],
1495 mpColorTable
[ nBlue
] ), bTransparent
);
1500 // BMP_FORMAT_48BIT_TC_RGB
1501 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 6 )
1503 sal_uInt8 nRed
= pTmp
[ 0 ];
1504 sal_uInt8 nGreen
= pTmp
[ 2 ];
1505 sal_uInt8 nBlue
= pTmp
[ 4 ];
1506 bool bTransparent
= ( ( nRed
== mnTransRed
)
1507 && ( nGreen
== mnTransGreen
)
1508 && ( nBlue
== mnTransBlue
) );
1510 ImplSetTranspPixel( nY
, nX
, BitmapColor( mpColorTable
[ nRed
],
1511 mpColorTable
[ nGreen
],
1512 mpColorTable
[ nBlue
] ), bTransparent
);
1516 else // has RGB but neither alpha nor transparency
1518 // ScanlineFormat::N24BitTcRgb
1519 // only use DirectScanline when we have no preview shifting stuff and access to content
1520 const bool bDoDirectScanline(
1521 bCkeckDirectScanline
&& !nXStart
&& 1 == nXAdd
&& !mnPreviewShift
);
1522 const bool bCustomColorTable(mpColorTable
!= mpDefaultColorTable
);
1524 if(bDoDirectScanline
&& !mpScanline
)
1526 // allocate scanlines on demand, reused for next line
1527 #if OSL_DEBUG_LEVEL > 0
1528 mnAllocSizeScanline
= maOrigSize
.Width() * 3;
1530 mpScanline
= new sal_uInt8
[maOrigSize
.Width() * 3];
1533 if ( mnPngDepth
== 8 ) // maybe the source has 16 bit per sample
1535 if(bDoDirectScanline
)
1537 OSL_ENSURE(mpScanline
, "No Scanline allocated (!)");
1538 #if OSL_DEBUG_LEVEL > 0
1539 OSL_ENSURE(mnAllocSizeScanline
>= maOrigSize
.Width() * 3, "Allocated Scanline too small (!)");
1541 sal_uInt8
* pScanline(mpScanline
);
1543 for (long nX(0); nX
< maOrigSize
.Width(); nX
++, pTmp
+= 3)
1545 // prepare content line as BGR by reordering when copying
1546 if(bCustomColorTable
)
1548 *pScanline
++ = mpColorTable
[pTmp
[2]];
1549 *pScanline
++ = mpColorTable
[pTmp
[1]];
1550 *pScanline
++ = mpColorTable
[pTmp
[0]];
1554 *pScanline
++ = pTmp
[2];
1555 *pScanline
++ = pTmp
[1];
1556 *pScanline
++ = pTmp
[0];
1560 // copy scanline directly to bitmap for content; use the format which is able to
1561 // copy directly to BitmapBuffer
1562 mxAcc
->CopyScanline(nY
, mpScanline
, ScanlineFormat::N24BitTcBgr
, maOrigSize
.Width() * 3);
1566 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 3 )
1568 if(bCustomColorTable
)
1574 mpColorTable
[ pTmp
[ 0 ] ],
1575 mpColorTable
[ pTmp
[ 1 ] ],
1576 mpColorTable
[ pTmp
[ 2 ] ]));
1593 // BMP_FORMAT_48BIT_TC_RGB
1594 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1595 for ( long nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 6 )
1601 mpColorTable
[ pTmp
[ 0 ] ],
1602 mpColorTable
[ pTmp
[ 2 ] ],
1603 mpColorTable
[ pTmp
[ 4 ] ]));
1610 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY
, sal_uInt32 nX
, const BitmapColor
& rBitmapColor
)
1612 // TODO: get preview mode checks out of inner loop
1613 if( nX
& mnPreviewMask
)
1615 nX
>>= mnPreviewShift
;
1617 mxAcc
->SetPixel( nY
, nX
, rBitmapColor
);
1620 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY
, sal_uInt32 nX
, sal_uInt8 nPalIndex
)
1622 // TODO: get preview mode checks out of inner loop
1623 if( nX
& mnPreviewMask
)
1625 nX
>>= mnPreviewShift
;
1627 mxAcc
->SetPixelIndex(nY
, nX
, SanitizePaletteIndex(nPalIndex
, mxAcc
->GetPaletteEntryCount()));
1630 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY
, sal_uInt32 nX
, const BitmapColor
& rBitmapColor
, bool bTrans
)
1632 // TODO: get preview mode checks out of inner loop
1633 if( nX
& mnPreviewMask
)
1635 nX
>>= mnPreviewShift
;
1637 mxAcc
->SetPixel( nY
, nX
, rBitmapColor
);
1640 mpMaskAcc
->SetPixel( nY
, nX
, mcTranspColor
);
1642 mpMaskAcc
->SetPixel( nY
, nX
, mcOpaqueColor
);
1645 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY
, sal_uInt32 nX
,
1646 sal_uInt8 nPalIndex
, sal_uInt8 nAlpha
)
1648 // TODO: get preview mode checks out of inner loop
1649 if( nX
& mnPreviewMask
)
1651 nX
>>= mnPreviewShift
;
1653 mxAcc
->SetPixelIndex(nY
, nX
, SanitizePaletteIndex(nPalIndex
, mxAcc
->GetPaletteEntryCount()));
1654 mpMaskAcc
->SetPixel(nY
, nX
, BitmapColor(~nAlpha
));
1657 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY
, sal_uInt32 nX
,
1658 const BitmapColor
& rBitmapColor
, sal_uInt8 nAlpha
)
1660 // TODO: get preview mode checks out of inner loop
1661 if( nX
& mnPreviewMask
)
1663 nX
>>= mnPreviewShift
;
1665 mxAcc
->SetPixel( nY
, nX
, rBitmapColor
);
1668 mpMaskAcc
->SetPixel(nY
, nX
, BitmapColor(~nAlpha
));
1671 sal_uInt32
PNGReaderImpl::ImplReadsal_uInt32()
1674 nRet
= *maDataIter
++;
1676 nRet
|= *maDataIter
++;
1678 nRet
|= *maDataIter
++;
1680 nRet
|= *maDataIter
++;
1684 PNGReader::PNGReader(SvStream
& rIStream
) :
1685 mpImpl(new vcl::PNGReaderImpl(rIStream
))
1689 PNGReader::~PNGReader()
1693 BitmapEx
PNGReader::Read( const Size
& i_rPreviewSizeHint
)
1695 return mpImpl
->GetBitmapEx( i_rPreviewSizeHint
);
1698 const std::vector
< vcl::PNGReader::ChunkData
>& PNGReader::GetChunks() const
1700 return mpImpl
->GetAllChunks();
1703 void PNGReader::SetIgnoreGammaChunk(bool bIgnoreGammaChunk
)
1705 mpImpl
->SetIgnoreGammaChunk(bIgnoreGammaChunk
);
1710 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */