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 .
20 #include <vcl/pngread.hxx>
24 #include <rtl/alloc.h>
25 #include <tools/zcodec.hxx>
26 #include <tools/stream.hxx>
27 #include <vcl/bmpacc.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/alpha.hxx>
30 #include <osl/endian.h>
35 #define PNGCHUNK_IHDR 0x49484452
36 #define PNGCHUNK_PLTE 0x504c5445
37 #define PNGCHUNK_IDAT 0x49444154
38 #define PNGCHUNK_IEND 0x49454e44
39 #define PNGCHUNK_bKGD 0x624b4744
40 #define PNGCHUNK_gAMA 0x67414d41
41 #define PNGCHUNK_pHYs 0x70485973
42 #define PNGCHUNK_tRNS 0x74524e53
44 #define VIEWING_GAMMA 2.35
45 #define DISPLAY_GAMMA 1.0
48 static const sal_uInt8 mpDefaultColorTable
[ 256 ] =
49 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
50 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
51 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
52 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
54 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
55 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
56 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
58 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
59 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
60 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
61 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
62 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
63 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
64 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
70 SvStream
& mrPNGStream
;
71 SvStreamEndian mnOrigStreamMode
;
73 std::vector
<vcl::PNGReader::ChunkData
> maChunkSeq
;
74 std::vector
<vcl::PNGReader::ChunkData
>::iterator maChunkIter
;
75 std::vector
<sal_uInt8
>::iterator maDataIter
;
78 BitmapWriteAccess
* mpAcc
;
80 AlphaMask
* mpAlphaMask
;
81 BitmapWriteAccess
* mpMaskAcc
;
83 sal_uInt8
* mpInflateInBuf
; // as big as the size of a scanline + alphachannel + 1
84 sal_uInt8
* mpScanPrior
; // pointer to the latest scanline
85 sal_uInt8
* mpTransTab
; // for transparency in images with palette colortype
86 sal_uInt8
* mpScanCurrent
; // pointer into the current scanline
87 sal_uInt8
* mpColorTable
;
88 sal_Size mnStreamSize
; // estimate of PNG file size
89 sal_uInt32 mnChunkType
; // Type of current PNG chunk
90 sal_Int32 mnChunkLen
; // Length of current PNG chunk
91 Size maOrigSize
; // pixel size of the full image
92 Size maTargetSize
; // pixel size of the result image
93 Size maPhysSize
; // preferred size in MAP_100TH_MM units
94 sal_uInt32 mnBPP
; // number of bytes per pixel
95 sal_uInt32 mnScansize
; // max size of scanline
96 sal_uInt32 mnYpos
; // latest y position in full image
97 int mnPass
; // if interlaced the latest pass ( 1..7 ) else 7
98 sal_uInt32 mnXStart
; // the starting X for the current pass
99 sal_uInt32 mnXAdd
; // the increment for input images X coords for the current pass
100 sal_uInt32 mnYAdd
; // the increment for input images Y coords for the current pass
101 int mnPreviewShift
; // shift to convert orig image coords into preview image coords
102 int mnPreviewMask
; // == ((1 << mnPreviewShift) - 1)
103 sal_uInt16 mnTargetDepth
; // pixel depth of target bitmap
104 sal_uInt8 mnTransRed
;
105 sal_uInt8 mnTransGreen
;
106 sal_uInt8 mnTransBlue
;
107 sal_uInt8 mnPngDepth
; // pixel depth of PNG data
108 sal_uInt8 mnColorType
;
109 sal_uInt8 mnCompressionType
;
110 sal_uInt8 mnFilterType
;
111 sal_uInt8 mnInterlaceType
;
112 BitmapColor mcTranspColor
; // transparency mask's transparency "color"
113 BitmapColor mcOpaqueColor
; // transparency mask's opaque "color"
114 bool mbTransparent
: 1; // graphic includes an tRNS Chunk or an alpha Channel
115 bool mbAlphaChannel
: 1; // is true for ColorType 4 and 6
116 bool mbRGBTriple
: 1;
117 bool mbPalette
: 1; // false if we need a Palette
118 bool mbGrayScale
: 1;
119 bool mbzCodecInUse
: 1;
121 bool mbIDAT
: 1; // true if finished with enough IDAT chunks
122 bool mbGamma
: 1; // true if Gamma Correction available
123 bool mbpHYs
: 1; // true if pysical size of pixel available
124 bool mbIgnoreGammaChunk
: 1;
126 #if OSL_DEBUG_LEVEL > 0
127 // do some checks in debug mode
128 sal_Int32 mnAllocSizeScanline
;
129 sal_Int32 mnAllocSizeScanlineAlpha
;
131 // the temporary Scanline (and alpha) for direct scanline copy to Bitmap
132 sal_uInt8
* mpScanline
;
133 sal_uInt8
* mpScanlineAlpha
;
135 bool ReadNextChunk();
136 void ReadRemainingChunks();
138 void ImplSetPixel( sal_uInt32 y
, sal_uInt32 x
, const BitmapColor
& );
139 void ImplSetPixel( sal_uInt32 y
, sal_uInt32 x
, sal_uInt8 nPalIndex
);
140 void ImplSetTranspPixel( sal_uInt32 y
, sal_uInt32 x
, const BitmapColor
&, bool bTrans
);
141 void ImplSetAlphaPixel( sal_uInt32 y
, sal_uInt32 x
, sal_uInt8 nPalIndex
, sal_uInt8 nAlpha
);
142 void ImplSetAlphaPixel( sal_uInt32 y
, sal_uInt32 x
, const BitmapColor
&, sal_uInt8 nAlpha
);
144 bool ImplPreparePass();
145 void ImplApplyFilter();
146 void ImplDrawScanline( sal_uInt32 nXStart
, sal_uInt32 nXAdd
);
147 bool ImplReadTransparent();
149 void ImplGetBackground();
150 sal_uInt8
ImplScaleColor();
151 bool ImplReadHeader( const Size
& rPreviewSizeHint
);
152 bool ImplReadPalette();
153 void ImplGetGrayPalette( sal_uInt16
);
154 sal_uInt32
ImplReadsal_uInt32();
158 explicit PNGReaderImpl( SvStream
& );
161 BitmapEx
GetBitmapEx( const Size
& rPreviewSizeHint
);
162 const std::vector
<vcl::PNGReader::ChunkData
>& GetAllChunks();
163 void SetIgnoreGammaChunk( bool bIgnore
){ mbIgnoreGammaChunk
= bIgnore
; };
166 PNGReaderImpl::PNGReaderImpl( SvStream
& rPNGStream
)
167 : mrPNGStream( rPNGStream
),
171 mpAlphaMask ( NULL
),
173 mpInflateInBuf ( NULL
),
174 mpScanPrior ( NULL
),
176 mpScanCurrent ( NULL
),
177 mpColorTable ( const_cast<sal_uInt8
*>(mpDefaultColorTable
) ),
193 mnCompressionType( 0 ),
195 mnInterlaceType ( 0 ),
196 mbTransparent( false ),
197 mbAlphaChannel( false ),
198 mbRGBTriple( false ),
200 mbGrayScale( false ),
201 mbzCodecInUse ( false ),
206 mbIgnoreGammaChunk ( false ),
207 #if OSL_DEBUG_LEVEL > 0
208 mnAllocSizeScanline(0),
209 mnAllocSizeScanlineAlpha(0),
214 // prepare the PNG data stream
215 mnOrigStreamMode
= mrPNGStream
.GetEndian();
216 mrPNGStream
.SetEndian( SvStreamEndian::BIG
);
218 // prepare the chunk reader
219 maChunkSeq
.reserve( 16 );
220 maChunkIter
= maChunkSeq
.begin();
222 // estimate PNG file size (to allow sanity checks)
223 const sal_Size nStreamPos
= mrPNGStream
.Tell();
224 mrPNGStream
.Seek( STREAM_SEEK_TO_END
);
225 mnStreamSize
= mrPNGStream
.Tell();
226 mrPNGStream
.Seek( nStreamPos
);
228 // check the PNG header magic
229 sal_uInt32 nDummy
= 0;
230 mrPNGStream
.ReadUInt32( nDummy
);
231 mbStatus
= (nDummy
== 0x89504e47);
232 mrPNGStream
.ReadUInt32( nDummy
);
233 mbStatus
= (nDummy
== 0x0d0a1a0a) && mbStatus
;
236 mnPreviewMask
= (1 << mnPreviewShift
) - 1;
239 PNGReaderImpl::~PNGReaderImpl()
241 mrPNGStream
.SetEndian( mnOrigStreamMode
);
244 mpZCodec
.EndCompression();
246 if( mpColorTable
!= mpDefaultColorTable
)
247 delete[] mpColorTable
;
253 delete[] mpInflateInBuf
;
254 delete[] mpScanPrior
;
257 delete[] mpScanlineAlpha
;
260 bool PNGReaderImpl::ReadNextChunk()
262 if( maChunkIter
== maChunkSeq
.end() )
264 // get the next chunk from the stream
266 // unless we are at the end of the PNG stream
267 if( mrPNGStream
.IsEof() || (mrPNGStream
.GetError() != ERRCODE_NONE
) )
269 if( !maChunkSeq
.empty() && (maChunkSeq
.back().nType
== PNGCHUNK_IEND
) )
272 PNGReader::ChunkData aDummyChunk
;
273 maChunkIter
= maChunkSeq
.insert( maChunkSeq
.end(), aDummyChunk
);
274 PNGReader::ChunkData
& rChunkData
= *maChunkIter
;
276 // read the chunk header
277 mrPNGStream
.ReadInt32( mnChunkLen
).ReadUInt32( mnChunkType
);
278 rChunkData
.nType
= mnChunkType
;
280 // fdo#61847 truncate over-long, trailing chunks
281 const sal_Size nStreamPos
= mrPNGStream
.Tell();
282 if( mnChunkLen
< 0 || nStreamPos
+ mnChunkLen
>= mnStreamSize
)
283 mnChunkLen
= mnStreamSize
- nStreamPos
;
285 // calculate chunktype CRC (swap it back to original byte order)
286 sal_uInt32 nChunkType
= mnChunkType
;
287 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
288 nChunkType
= OSL_SWAPDWORD( nChunkType
);
290 sal_uInt32 nCRC32
= rtl_crc32( 0, &nChunkType
, 4 );
292 // read the chunk data and check the CRC
293 if( mnChunkLen
&& !mrPNGStream
.IsEof() )
295 rChunkData
.aData
.resize( mnChunkLen
);
297 sal_Int32 nBytesRead
= 0;
299 sal_uInt8
* pPtr
= &rChunkData
.aData
[ nBytesRead
];
300 nBytesRead
+= mrPNGStream
.Read( pPtr
, mnChunkLen
- nBytesRead
);
301 } while ( ( nBytesRead
< mnChunkLen
) && ( mrPNGStream
.GetError() == ERRCODE_NONE
) );
303 nCRC32
= rtl_crc32( nCRC32
, &rChunkData
.aData
[ 0 ], mnChunkLen
);
304 maDataIter
= rChunkData
.aData
.begin();
306 sal_uInt32
nCheck(0);
307 mrPNGStream
.ReadUInt32( nCheck
);
308 if( nCRC32
!= nCheck
)
313 // the next chunk was already read
314 mnChunkType
= (*maChunkIter
).nType
;
315 mnChunkLen
= (*maChunkIter
).aData
.size();
316 maDataIter
= (*maChunkIter
).aData
.begin();
320 if( mnChunkType
== PNGCHUNK_IEND
)
325 // read the remaining chunks from mrPNGStream
326 void PNGReaderImpl::ReadRemainingChunks()
328 while( ReadNextChunk() ) ;
331 const std::vector
< vcl::PNGReader::ChunkData
>& PNGReaderImpl::GetAllChunks()
333 ReadRemainingChunks();
337 BitmapEx
PNGReaderImpl::GetBitmapEx( const Size
& rPreviewSizeHint
)
339 // reset to the first chunk
340 maChunkIter
= maChunkSeq
.begin();
342 // first chunk must be IDHR
343 if( mbStatus
&& ReadNextChunk() )
345 if (mnChunkType
== PNGCHUNK_IHDR
)
346 mbStatus
= ImplReadHeader( rPreviewSizeHint
);
351 // parse the remaining chunks
352 while (mbStatus
&& !mbIDAT
&& ReadNextChunk())
354 switch( mnChunkType
)
358 mbStatus
= false; //IHDR should only appear as the first chunk
362 case PNGCHUNK_gAMA
: // the gamma chunk must precede
363 { // the 'IDAT' and also the 'PLTE'(if available )
364 if ( !mbIgnoreGammaChunk
&& !mbIDAT
)
372 mbStatus
= ImplReadPalette();
378 if ( !mbIDAT
) // the tRNS chunk must precede the IDAT
379 mbStatus
= ImplReadTransparent();
383 case PNGCHUNK_bKGD
: // the background chunk must appear
385 if ( !mbIDAT
&& mbPalette
) // before the 'IDAT' and after the
386 ImplGetBackground(); // PLTE(if available ) chunk.
392 if ( !mpInflateInBuf
) // taking care that the header has properly been read
394 else if ( !mbIDAT
) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes
401 if ( !mbIDAT
&& mnChunkLen
== 9 )
403 sal_uInt32 nXPixelPerMeter
= ImplReadsal_uInt32();
404 sal_uInt32 nYPixelPerMeter
= ImplReadsal_uInt32();
406 sal_uInt8 nUnitSpecifier
= *maDataIter
++;
407 if( (nUnitSpecifier
== 1) && nXPixelPerMeter
&& nYPixelPerMeter
)
411 // convert into MAP_100TH_MM
412 maPhysSize
.Width() = (sal_Int32
)( (100000.0 * maOrigSize
.Width()) / nXPixelPerMeter
);
413 maPhysSize
.Height() = (sal_Int32
)( (100000.0 * maOrigSize
.Height()) / nYPixelPerMeter
);
420 mbStatus
= mbIDAT
; // there is a problem if the image is not complete yet
425 // release write access of the bitmaps
427 Bitmap::ReleaseAccess( mpAcc
), mpAcc
= NULL
;
432 mpAlphaMask
->ReleaseAccess( mpMaskAcc
);
433 else if ( mpMaskBmp
)
434 Bitmap::ReleaseAccess( mpMaskAcc
);
439 // return the resulting BitmapEx
442 if( !mbStatus
|| !mbIDAT
)
447 aRet
= BitmapEx( *mpBmp
, *mpAlphaMask
);
448 else if ( mpMaskBmp
)
449 aRet
= BitmapEx( *mpBmp
, *mpMaskBmp
);
453 if ( mbpHYs
&& maPhysSize
.Width() && maPhysSize
.Height() )
455 aRet
.SetPrefMapMode( MAP_100TH_MM
);
456 aRet
.SetPrefSize( maPhysSize
);
462 bool PNGReaderImpl::ImplReadHeader( const Size
& rPreviewSizeHint
)
464 if( mnChunkLen
< 13 )
467 maOrigSize
.Width() = ImplReadsal_uInt32();
468 maOrigSize
.Height() = ImplReadsal_uInt32();
470 if (maOrigSize
.Width() <= 0 || maOrigSize
.Height() <= 0)
473 mnPngDepth
= *(maDataIter
++);
474 mnColorType
= *(maDataIter
++);
476 mnCompressionType
= *(maDataIter
++);
477 if( mnCompressionType
!= 0 ) // unknown compression type
480 mnFilterType
= *(maDataIter
++);
481 if( mnFilterType
!= 0 ) // unknown filter type
484 mnInterlaceType
= *(maDataIter
++);
485 switch ( mnInterlaceType
) // filter type valid ?
487 case 0 : // progressive image
490 case 1 : // Adam7-interlaced image
498 mbIDAT
= mbAlphaChannel
= mbTransparent
= false;
499 mbGrayScale
= mbRGBTriple
= false;
500 mnTargetDepth
= mnPngDepth
;
501 sal_uInt64 nScansize64
= ( ( static_cast< sal_uInt64
>( maOrigSize
.Width() ) * mnPngDepth
) + 7 ) >> 3;
503 // valid color types are 0,2,3,4 & 6
504 switch ( mnColorType
)
506 case 0 : // each pixel is a grayscale
508 switch ( mnPngDepth
)
510 case 2 : // 2bit target not available -> use four bits
511 mnTargetDepth
= 4; // we have to expand the bitmap
515 mnTargetDepth
= 8; // we have to reduce the bitmap
528 case 2 : // each pixel is an RGB triple
532 switch ( mnPngDepth
)
534 case 16 : // we have to reduce the bitmap
544 case 3 : // each pixel is a palette index
546 switch ( mnPngDepth
)
549 mnTargetDepth
= 4; // we have to expand the bitmap
562 case 4 : // each pixel is a grayscale sample followed by an alpha sample
565 mbAlphaChannel
= true;
566 switch ( mnPngDepth
)
569 mnTargetDepth
= 8; // we have to reduce the bitmap
579 case 6 : // each pixel is an RGB triple followed by an alpha sample
583 mbAlphaChannel
= true;
586 case 16 : // we have to reduce the bitmap
600 mnBPP
= static_cast< sal_uInt32
>( nScansize64
/ maOrigSize
.Width() );
604 nScansize64
++; // each scanline includes one filterbyte
606 if ( nScansize64
> SAL_MAX_UINT32
)
609 mnScansize
= static_cast< sal_uInt32
>( nScansize64
);
611 // calculate target size from original size and the preview hint
612 if( rPreviewSizeHint
.Width() || rPreviewSizeHint
.Height() )
614 Size
aPreviewSize( rPreviewSizeHint
.Width(), rPreviewSizeHint
.Height() );
615 maTargetSize
= maOrigSize
;
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 );
627 if( aPreviewSize
.Width() < maOrigSize
.Width() && aPreviewSize
.Height() < maOrigSize
.Height() ) {
628 OSL_TRACE("preview size %ldx%ld", aPreviewSize
.Width(), aPreviewSize
.Height() );
630 for( int i
= 1; i
< 5; ++i
)
632 if( (maTargetSize
.Width() >> i
) < aPreviewSize
.Width() )
634 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
= new Bitmap( maTargetSize
, mnTargetDepth
);
665 mpAcc
= mpBmp
->AcquireWriteAccess();
669 if ( mbAlphaChannel
)
671 mpAlphaMask
= new AlphaMask( maTargetSize
);
672 mpAlphaMask
->Erase( 128 );
673 mpMaskAcc
= mpAlphaMask
->AcquireWriteAccess();
679 ImplGetGrayPalette( mnPngDepth
);
686 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth
)
691 sal_uInt16 nPaletteEntryCount
= 1 << nBitDepth
;
692 sal_uInt32 nAdd
= nBitDepth
? 256 / (nPaletteEntryCount
- 1) : 0;
694 // no bitdepth==2 available
695 // but bitdepth==4 with two unused bits is close enough
697 nPaletteEntryCount
= 16;
699 mpAcc
->SetPaletteEntryCount( nPaletteEntryCount
);
700 for ( sal_uInt32 i
= 0, nStart
= 0; nStart
< 256; i
++, nStart
+= nAdd
)
701 mpAcc
->SetPaletteColor( (sal_uInt16
)i
, BitmapColor( mpColorTable
[ nStart
],
702 mpColorTable
[ nStart
], mpColorTable
[ nStart
] ) );
705 bool PNGReaderImpl::ImplReadPalette()
707 sal_uInt16 nCount
= static_cast<sal_uInt16
>( mnChunkLen
/ 3 );
709 if ( ( ( mnChunkLen
% 3 ) == 0 ) && ( ( 0 < nCount
) && ( nCount
<= 256 ) ) && mpAcc
)
712 mpAcc
->SetPaletteEntryCount( (sal_uInt16
) nCount
);
714 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
716 sal_uInt8 nRed
= mpColorTable
[ *maDataIter
++ ];
717 sal_uInt8 nGreen
= mpColorTable
[ *maDataIter
++ ];
718 sal_uInt8 nBlue
= mpColorTable
[ *maDataIter
++ ];
719 mpAcc
->SetPaletteColor( i
, Color( nRed
, nGreen
, nBlue
) );
728 bool PNGReaderImpl::ImplReadTransparent()
730 bool bNeedAlpha
= false;
732 if ( mpTransTab
== NULL
)
734 switch ( mnColorType
)
738 if ( mnChunkLen
== 2 )
740 mpTransTab
= new sal_uInt8
[ 256 ];
741 memset( mpTransTab
, 0xff, 256);
742 // color type 0 and 4 is always greyscale,
743 // so the return value can be used as index
744 sal_uInt8 nIndex
= ImplScaleColor();
745 mpTransTab
[ nIndex
] = 0;
746 mbTransparent
= true;
753 if ( mnChunkLen
== 6 )
755 mnTransRed
= ImplScaleColor();
756 mnTransGreen
= ImplScaleColor();
757 mnTransBlue
= ImplScaleColor();
758 mbTransparent
= true;
765 if ( mnChunkLen
<= 256 )
767 mbTransparent
= true;
768 mpTransTab
= new sal_uInt8
[ 256 ];
769 memset( mpTransTab
, 0xff, 256 );
772 memcpy( mpTransTab
, &(*maDataIter
), mnChunkLen
);
773 maDataIter
+= mnChunkLen
;
774 // need alpha transparency if not on/off masking
775 for( int i
= 0; i
< mnChunkLen
; ++i
)
776 bNeedAlpha
|= (mpTransTab
[i
]!=0x00) && (mpTransTab
[i
]!=0xFF);
784 if( mbTransparent
&& !mbAlphaChannel
&& !mpMaskBmp
)
788 mpAlphaMask
= new AlphaMask( maTargetSize
);
789 mpMaskAcc
= mpAlphaMask
->AcquireWriteAccess();
793 mpMaskBmp
= new Bitmap( maTargetSize
, 1 );
794 mpMaskAcc
= mpMaskBmp
->AcquireWriteAccess();
796 mbTransparent
= (mpMaskAcc
!= NULL
);
799 mcOpaqueColor
= BitmapColor( 0x00 );
800 mcTranspColor
= BitmapColor( 0xFF );
801 mpMaskAcc
->Erase( 0x00 );
807 void PNGReaderImpl::ImplGetGamma()
812 sal_uInt32 nGammaValue
= ImplReadsal_uInt32();
813 double fGamma
= ( ( VIEWING_GAMMA
/ DISPLAY_GAMMA
) * ( (double)nGammaValue
/ 100000 ) );
814 double fInvGamma
= ( fGamma
<= 0.0 || fGamma
> 10.0 ) ? 1.0 : ( 1.0 / fGamma
);
816 if ( fInvGamma
!= 1.0 )
820 if ( mpColorTable
== mpDefaultColorTable
)
821 mpColorTable
= new sal_uInt8
[ 256 ];
823 for ( sal_Int32 i
= 0; i
< 256; i
++ )
824 mpColorTable
[ i
] = (sal_uInt8
)(pow((double)i
/255.0, fInvGamma
) * 255.0 + 0.5);
827 ImplGetGrayPalette( mnPngDepth
);
831 void PNGReaderImpl::ImplGetBackground()
833 switch ( mnColorType
)
837 if ( mnChunkLen
== 1 )
839 sal_uInt16 nCol
= *maDataIter
++;
840 if ( nCol
< mpAcc
->GetPaletteEntryCount() )
842 mpAcc
->Erase( mpAcc
->GetPaletteColor( (sal_uInt8
)nCol
) );
852 if ( mnChunkLen
== 2 )
854 // the color type 0 and 4 is always greyscale,
855 // so the return value can be used as index
856 sal_uInt8 nIndex
= ImplScaleColor();
857 mpAcc
->Erase( mpAcc
->GetPaletteColor( nIndex
) );
865 if ( mnChunkLen
== 6 )
867 sal_uInt8 nRed
= ImplScaleColor();
868 sal_uInt8 nGreen
= ImplScaleColor();
869 sal_uInt8 nBlue
= ImplScaleColor();
870 mpAcc
->Erase( Color( nRed
, nGreen
, nBlue
) );
877 // for color type 0 and 4 (greyscale) the return value is always index to the color
878 // 2 and 6 (RGB) the return value is always the 8 bit color component
879 sal_uInt8
PNGReaderImpl::ImplScaleColor()
881 sal_uInt32 nMask
= ( ( 1 << mnPngDepth
) - 1 );
882 sal_uInt16 nCol
= ( *maDataIter
++ << 8 );
884 nCol
+= *maDataIter
++ & (sal_uInt16
)nMask
;
886 if ( mnPngDepth
> 8 ) // convert 16bit graphics to 8
889 return (sal_uInt8
) nCol
;
892 // ImplReadIDAT reads as much image data as needed
894 void PNGReaderImpl::ImplReadIDAT()
898 if ( !mbzCodecInUse
)
900 mbzCodecInUse
= true;
901 mpZCodec
.BeginCompression( ZCODEC_NO_COMPRESSION
, true );
903 mpZCodec
.SetBreak( mnChunkLen
);
904 SvMemoryStream
aIStrm( &(*maDataIter
), mnChunkLen
, StreamMode::READ
);
906 while ( ( mpZCodec
.GetBreak() ) )
908 // get bytes needed to fill the current scanline
909 sal_Int32 nToRead
= mnScansize
- (mpScanCurrent
- mpInflateInBuf
);
910 sal_Int32 nRead
= mpZCodec
.ReadAsynchron( aIStrm
, mpScanCurrent
, nToRead
);
916 if ( nRead
< nToRead
)
918 mpScanCurrent
+= nRead
; // more ZStream data in the next IDAT chunk
921 else // this scanline is Finished
923 mpScanCurrent
= mpInflateInBuf
;
926 ImplDrawScanline( mnXStart
, mnXAdd
);
930 if ( mnYpos
>= (sal_uInt32
)maOrigSize
.Height() )
932 if( (mnPass
< 7) && mnInterlaceType
)
933 if( ImplPreparePass() )
943 mpZCodec
.EndCompression();
944 mbzCodecInUse
= false;
948 bool PNGReaderImpl::ImplPreparePass()
950 struct InterlaceParams
{ int mnXStart
, mnYStart
, mnXAdd
, mnYAdd
; };
951 static const InterlaceParams aInterlaceParams
[8] =
956 { 0, 0, 8, 8 }, // pass 1
957 { 4, 0, 8, 8 }, // pass 2
958 { 0, 4, 4, 8 }, // pass 3
959 { 2, 0, 4, 4 }, // pass 4
960 { 0, 2, 2, 4 }, // pass 5
961 { 1, 0, 2, 2 }, // pass 6
962 { 0, 1, 1, 2 } // pass 7
965 const InterlaceParams
* pParam
= &aInterlaceParams
[ 0 ];
966 if( mnInterlaceType
)
968 while( ++mnPass
<= 7 )
970 pParam
= &aInterlaceParams
[ mnPass
];
972 // skip this pass if the original image is too small for it
973 if( (pParam
->mnXStart
< maOrigSize
.Width())
974 && (pParam
->mnYStart
< maOrigSize
.Height()) )
980 // skip the last passes if possible (for scaled down target images)
981 if( mnPreviewMask
& (pParam
->mnXStart
| pParam
->mnYStart
) )
985 mnYpos
= pParam
->mnYStart
;
986 mnXStart
= pParam
->mnXStart
;
987 mnXAdd
= pParam
->mnXAdd
;
988 mnYAdd
= pParam
->mnYAdd
;
990 // in Interlace mode the size of scanline is not constant
991 // so first we calculate the number of entrys
992 long nScanWidth
= (maOrigSize
.Width() - mnXStart
+ mnXAdd
- 1) / mnXAdd
;
993 mnScansize
= nScanWidth
;
996 mnScansize
= 3 * nScanWidth
;
999 mnScansize
+= nScanWidth
;
1001 // convert to width in bytes
1002 mnScansize
= ( mnScansize
*mnPngDepth
+ 7 ) >> 3;
1004 ++mnScansize
; // scan size also needs room for the filtertype byte
1005 memset( mpScanPrior
, 0, mnScansize
);
1010 // ImplApplyFilter writes the complete Scanline (nY)
1011 // in interlace mode the parameter nXStart and nXAdd are non-zero
1013 void PNGReaderImpl::ImplApplyFilter()
1015 OSL_ASSERT( mnScansize
>= mnBPP
+ 1 );
1016 const sal_uInt8
* const pScanEnd
= mpInflateInBuf
+ mnScansize
;
1018 sal_uInt8 nFilterType
= *mpInflateInBuf
; // the filter type may change each scanline
1019 switch ( nFilterType
)
1021 default: // unknown Scanline Filter Type
1022 case 0: // Filter Type "None"
1023 // we let the pixels pass and display the data unfiltered
1026 case 1: // Scanline Filter Type "Sub"
1028 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1029 const sal_uInt8
* p2
= p1
;
1033 while (p1
< pScanEnd
)
1035 *p1
= static_cast<sal_uInt8
>( *p1
+ *(p2
++) );
1041 case 2: // Scanline Filter Type "Up"
1043 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1044 const sal_uInt8
* p2
= mpScanPrior
+ 1;
1046 // use pixels from prior line
1047 while( p1
< pScanEnd
)
1049 *p1
= static_cast<sal_uInt8
>( *p1
+ *(p2
++) );
1055 case 3: // Scanline Filter Type "Average"
1057 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1058 const sal_uInt8
* p2
= mpScanPrior
+ 1;
1059 const sal_uInt8
* p3
= p1
;
1061 // use one pixel from prior line
1062 for( int n
= mnBPP
; --n
>= 0; ++p1
, ++p2
)
1063 *p1
= static_cast<sal_uInt8
>( *p1
+ (*p2
>> 1) );
1065 // predict by averaging the left and prior line pixels
1066 while( p1
< pScanEnd
)
1068 *p1
= static_cast<sal_uInt8
>( *p1
+ ((*(p2
++) + *(p3
++)) >> 1) );
1074 case 4: // Scanline Filter Type "PaethPredictor"
1076 sal_uInt8
* p1
= mpInflateInBuf
+ 1;
1077 const sal_uInt8
* p2
= mpScanPrior
+ 1;
1078 const sal_uInt8
* p3
= p1
;
1079 const sal_uInt8
* p4
= p2
;
1081 // use one pixel from prior line
1082 for( int n
= mnBPP
; --n
>= 0; ++p1
)
1083 *p1
= static_cast<sal_uInt8
>( *p1
+ *(p2
++) );
1085 // predict by using the left and the prior line pixels
1086 while( p1
< pScanEnd
)
1092 int npa
= nb
- (int)nc
;
1093 int npb
= na
- (int)nc
;
1094 int npc
= npa
+ npb
;
1108 *p1
= static_cast<sal_uInt8
>( *p1
+ na
);
1115 memcpy( mpScanPrior
, mpInflateInBuf
, mnScansize
);
1118 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1119 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1121 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart
, sal_uInt32 nXAdd
)
1123 // optimization for downscaling
1124 if( mnYpos
& mnPreviewMask
)
1126 if( nXStart
& mnPreviewMask
)
1129 // convert nY to pixel units in the target image
1130 // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1131 const sal_uInt32 nY
= mnYpos
>> mnPreviewShift
;
1133 const sal_uInt8
* pTmp
= mpInflateInBuf
+ 1;
1134 if ( mpAcc
->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1136 switch ( mpAcc
->GetBitCount() )
1140 if ( mbTransparent
)
1142 for ( sal_Int32 nX
= nXStart
, nShift
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
)
1145 nShift
= (nShift
- 1) & 7;
1149 nCol
= static_cast<sal_uInt8
>( *pTmp
>> nShift
);
1152 ImplSetAlphaPixel( nY
, nX
, nCol
, mpTransTab
[ nCol
] );
1156 { // BMP_FORMAT_1BIT_MSB_PAL
1157 for ( sal_Int32 nX
= nXStart
, nShift
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
)
1159 nShift
= (nShift
- 1) & 7;
1165 nCol
= static_cast<sal_uInt8
>( *pTmp
>> nShift
);
1168 ImplSetPixel( nY
, nX
, nCol
);
1176 if ( mbTransparent
)
1178 if ( mnPngDepth
== 4 ) // check if source has a two bit pixel format
1180 for ( sal_Int32 nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, ++nXIndex
)
1184 ImplSetAlphaPixel( nY
, nX
, *pTmp
& 0x0f, mpTransTab
[ *pTmp
& 0x0f ] );
1189 ImplSetAlphaPixel( nY
, nX
, ( *pTmp
>> 4 ) & 0x0f, mpTransTab
[ *pTmp
>> 4 ] );
1193 else // if ( mnPngDepth == 2 )
1195 for ( sal_Int32 nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, nXIndex
++ )
1198 switch( nXIndex
& 3 )
1205 nCol
= ( *pTmp
>> 4 ) & 0x03 ;
1209 nCol
= ( *pTmp
>> 2 ) & 0x03;
1213 nCol
= ( *pTmp
++ ) & 0x03;
1216 default: // get rid of nCol uninitialized warning
1221 ImplSetAlphaPixel( nY
, nX
, nCol
, mpTransTab
[ nCol
] );
1227 if ( mnPngDepth
== 4 ) // maybe the source is a two bitmap graphic
1228 { // BMP_FORMAT_4BIT_LSN_PAL
1229 for ( sal_Int32 nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, nXIndex
++ )
1232 ImplSetPixel( nY
, nX
, *pTmp
++ & 0x0f );
1234 ImplSetPixel( nY
, nX
, ( *pTmp
>> 4 ) & 0x0f );
1237 else // if ( mnPngDepth == 2 )
1239 for ( sal_Int32 nX
= nXStart
, nXIndex
= 0; nX
< maOrigSize
.Width(); nX
+= nXAdd
, nXIndex
++ )
1241 switch( nXIndex
& 3 )
1244 ImplSetPixel( nY
, nX
, *pTmp
>> 6 );
1248 ImplSetPixel( nY
, nX
, ( *pTmp
>> 4 ) & 0x03 );
1252 ImplSetPixel( nY
, nX
, ( *pTmp
>> 2 ) & 0x03 );
1256 ImplSetPixel( nY
, nX
, *pTmp
++ & 0x03 );
1267 if ( mbAlphaChannel
)
1269 if ( mnPngDepth
== 8 ) // maybe the source is a 16 bit grayscale
1271 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 2 )
1272 ImplSetAlphaPixel( nY
, nX
, pTmp
[ 0 ], pTmp
[ 1 ] );
1276 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 4 )
1277 ImplSetAlphaPixel( nY
, nX
, pTmp
[ 0 ], pTmp
[ 2 ] );
1280 else if ( mbTransparent
)
1282 if ( mnPngDepth
== 8 ) // maybe the source is a 16 bit grayscale
1284 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
++ )
1285 ImplSetAlphaPixel( nY
, nX
, *pTmp
, mpTransTab
[ *pTmp
] );
1289 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 2 )
1290 ImplSetAlphaPixel( nY
, nX
, *pTmp
, mpTransTab
[ *pTmp
] );
1293 else // neither alpha nor transparency
1295 if ( mnPngDepth
== 8 ) // maybe the source is a 16 bit grayscale
1297 if( nXAdd
== 1 && mnPreviewShift
== 0 ) // copy raw line data if possible
1299 int nLineBytes
= maOrigSize
.Width();
1300 mpAcc
->CopyScanline( nY
, pTmp
, BMP_FORMAT_8BIT_PAL
, nLineBytes
);
1305 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
)
1306 ImplSetPixel( nY
, nX
, *pTmp
++ );
1311 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 2 )
1312 ImplSetPixel( nY
, nX
, *pTmp
);
1323 else // no palette => truecolor
1325 // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
1326 static bool bCkeckDirectScanline(true);
1328 if( mbAlphaChannel
)
1331 if ( mnPngDepth
== 8 ) // maybe the source has 16 bit per sample
1333 // BMP_FORMAT_32BIT_TC_RGBA
1334 // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha
1335 const bool bDoDirectScanline(
1336 bCkeckDirectScanline
&& !nXStart
&& 1 == nXAdd
&& !mnPreviewShift
&& mpMaskAcc
);
1337 const bool bCustomColorTable(mpColorTable
!= mpDefaultColorTable
);
1339 if(bDoDirectScanline
)
1341 // allocate scanlines on demand, reused for next line
1344 #if OSL_DEBUG_LEVEL > 0
1345 mnAllocSizeScanline
= maOrigSize
.Width() * 3;
1347 mpScanline
= new sal_uInt8
[maOrigSize
.Width() * 3];
1350 if(!mpScanlineAlpha
)
1352 #if OSL_DEBUG_LEVEL > 0
1353 mnAllocSizeScanlineAlpha
= maOrigSize
.Width();
1355 mpScanlineAlpha
= new sal_uInt8
[maOrigSize
.Width()];
1359 if(bDoDirectScanline
)
1361 OSL_ENSURE(mpScanline
, "No Scanline allocated (!)");
1362 OSL_ENSURE(mpScanlineAlpha
, "No ScanlineAlpha allocated (!)");
1363 OSL_ENSURE(mnAllocSizeScanline
>= maOrigSize
.Width() * 3, "Allocated Scanline too small (!)");
1364 OSL_ENSURE(mnAllocSizeScanlineAlpha
>= maOrigSize
.Width(), "Allocated ScanlineAlpha too small (!)");
1365 sal_uInt8
* pScanline(mpScanline
);
1366 sal_uInt8
* pScanlineAlpha(mpScanlineAlpha
);
1368 for (sal_Int32
nX(0); nX
< maOrigSize
.Width(); nX
++, pTmp
+= 4)
1370 // prepare content line as BGR by reordering when copying
1371 // do not forget to invert alpha (source is alpha, target is opacity)
1372 if(bCustomColorTable
)
1374 *pScanline
++ = mpColorTable
[pTmp
[2]];
1375 *pScanline
++ = mpColorTable
[pTmp
[1]];
1376 *pScanline
++ = mpColorTable
[pTmp
[0]];
1377 *pScanlineAlpha
++ = ~pTmp
[3];
1381 *pScanline
++ = pTmp
[2];
1382 *pScanline
++ = pTmp
[1];
1383 *pScanline
++ = pTmp
[0];
1384 *pScanlineAlpha
++ = ~pTmp
[3];
1388 // copy scanlines directly to bitmaps for content and alpha; use the formats which
1389 // are able to copy directly to BitmapBuffer
1390 mpAcc
->CopyScanline(nY
, mpScanline
, BMP_FORMAT_24BIT_TC_BGR
, maOrigSize
.Width() * 3);
1391 mpMaskAcc
->CopyScanline(nY
, mpScanlineAlpha
, BMP_FORMAT_8BIT_PAL
, maOrigSize
.Width());
1395 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 4 )
1397 if(bCustomColorTable
)
1403 mpColorTable
[ pTmp
[ 0 ] ],
1404 mpColorTable
[ pTmp
[ 1 ] ],
1405 mpColorTable
[ pTmp
[ 2 ] ]),
1424 // BMP_FORMAT_64BIT_TC_RGBA
1425 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 8 )
1431 mpColorTable
[ pTmp
[ 0 ] ],
1432 mpColorTable
[ pTmp
[ 2 ] ],
1433 mpColorTable
[ pTmp
[ 4 ] ]),
1438 else if( mbTransparent
) // has RGB + transparency
1440 // BMP_FORMAT_24BIT_TC_RGB
1441 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1442 if ( mnPngDepth
== 8 ) // maybe the source has 16 bit per sample
1444 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 3 )
1446 sal_uInt8 nRed
= pTmp
[ 0 ];
1447 sal_uInt8 nGreen
= pTmp
[ 1 ];
1448 sal_uInt8 nBlue
= pTmp
[ 2 ];
1449 bool bTransparent
= ( ( nRed
== mnTransRed
)
1450 && ( nGreen
== mnTransGreen
)
1451 && ( nBlue
== mnTransBlue
) );
1453 ImplSetTranspPixel( nY
, nX
, BitmapColor( mpColorTable
[ nRed
],
1454 mpColorTable
[ nGreen
],
1455 mpColorTable
[ nBlue
] ), bTransparent
);
1460 // BMP_FORMAT_48BIT_TC_RGB
1461 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 6 )
1463 sal_uInt8 nRed
= pTmp
[ 0 ];
1464 sal_uInt8 nGreen
= pTmp
[ 2 ];
1465 sal_uInt8 nBlue
= pTmp
[ 4 ];
1466 bool bTransparent
= ( ( nRed
== mnTransRed
)
1467 && ( nGreen
== mnTransGreen
)
1468 && ( nBlue
== mnTransBlue
) );
1470 ImplSetTranspPixel( nY
, nX
, BitmapColor( mpColorTable
[ nRed
],
1471 mpColorTable
[ nGreen
],
1472 mpColorTable
[ nBlue
] ), bTransparent
);
1476 else // has RGB but neither alpha nor transparency
1478 // BMP_FORMAT_24BIT_TC_RGB
1479 // only use DirectScanline when we have no preview shifting stuff and access to content
1480 const bool bDoDirectScanline(
1481 bCkeckDirectScanline
&& !nXStart
&& 1 == nXAdd
&& !mnPreviewShift
);
1482 const bool bCustomColorTable(mpColorTable
!= mpDefaultColorTable
);
1484 if(bDoDirectScanline
&& !mpScanline
)
1486 // allocate scanlines on demand, reused for next line
1487 #if OSL_DEBUG_LEVEL > 0
1488 mnAllocSizeScanline
= maOrigSize
.Width() * 3;
1490 mpScanline
= new sal_uInt8
[maOrigSize
.Width() * 3];
1493 if ( mnPngDepth
== 8 ) // maybe the source has 16 bit per sample
1495 if(bDoDirectScanline
)
1497 OSL_ENSURE(mpScanline
, "No Scanline allocated (!)");
1498 OSL_ENSURE(mnAllocSizeScanline
>= maOrigSize
.Width() * 3, "Allocated Scanline too small (!)");
1499 sal_uInt8
* pScanline(mpScanline
);
1501 for (sal_Int32
nX(0); nX
< maOrigSize
.Width(); nX
++, pTmp
+= 3)
1503 // prepare content line as BGR by reordering when copying
1504 if(bCustomColorTable
)
1506 *pScanline
++ = mpColorTable
[pTmp
[2]];
1507 *pScanline
++ = mpColorTable
[pTmp
[1]];
1508 *pScanline
++ = mpColorTable
[pTmp
[0]];
1512 *pScanline
++ = pTmp
[2];
1513 *pScanline
++ = pTmp
[1];
1514 *pScanline
++ = pTmp
[0];
1518 // copy scanline directly to bitmap for content; use the format which is able to
1519 // copy directly to BitmapBuffer
1520 mpAcc
->CopyScanline(nY
, mpScanline
, BMP_FORMAT_24BIT_TC_BGR
, maOrigSize
.Width() * 3);
1524 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 3 )
1526 if(bCustomColorTable
)
1532 mpColorTable
[ pTmp
[ 0 ] ],
1533 mpColorTable
[ pTmp
[ 1 ] ],
1534 mpColorTable
[ pTmp
[ 2 ] ]));
1551 // BMP_FORMAT_48BIT_TC_RGB
1552 // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1553 for ( sal_Int32 nX
= nXStart
; nX
< maOrigSize
.Width(); nX
+= nXAdd
, pTmp
+= 6 )
1559 mpColorTable
[ pTmp
[ 0 ] ],
1560 mpColorTable
[ pTmp
[ 2 ] ],
1561 mpColorTable
[ pTmp
[ 4 ] ]));
1568 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY
, sal_uInt32 nX
, const BitmapColor
& rBitmapColor
)
1570 // TODO: get preview mode checks out of inner loop
1571 if( nX
& mnPreviewMask
)
1573 nX
>>= mnPreviewShift
;
1575 mpAcc
->SetPixel( nY
, nX
, rBitmapColor
);
1578 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY
, sal_uInt32 nX
, sal_uInt8 nPalIndex
)
1580 // TODO: get preview mode checks out of inner loop
1581 if( nX
& mnPreviewMask
)
1583 nX
>>= mnPreviewShift
;
1585 mpAcc
->SetPixelIndex( nY
, nX
, nPalIndex
);
1588 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY
, sal_uInt32 nX
, const BitmapColor
& rBitmapColor
, bool bTrans
)
1590 // TODO: get preview mode checks out of inner loop
1591 if( nX
& mnPreviewMask
)
1593 nX
>>= mnPreviewShift
;
1595 mpAcc
->SetPixel( nY
, nX
, rBitmapColor
);
1598 mpMaskAcc
->SetPixel( nY
, nX
, mcTranspColor
);
1600 mpMaskAcc
->SetPixel( nY
, nX
, mcOpaqueColor
);
1603 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY
, sal_uInt32 nX
,
1604 sal_uInt8 nPalIndex
, sal_uInt8 nAlpha
)
1606 // TODO: get preview mode checks out of inner loop
1607 if( nX
& mnPreviewMask
)
1609 nX
>>= mnPreviewShift
;
1611 mpAcc
->SetPixelIndex( nY
, nX
, nPalIndex
);
1612 mpMaskAcc
->SetPixelIndex( nY
, nX
, ~nAlpha
);
1615 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY
, sal_uInt32 nX
,
1616 const BitmapColor
& rBitmapColor
, sal_uInt8 nAlpha
)
1618 // TODO: get preview mode checks out of inner loop
1619 if( nX
& mnPreviewMask
)
1621 nX
>>= mnPreviewShift
;
1623 mpAcc
->SetPixel( nY
, nX
, rBitmapColor
);
1626 mpMaskAcc
->SetPixelIndex( nY
, nX
, ~nAlpha
);
1629 sal_uInt32
PNGReaderImpl::ImplReadsal_uInt32()
1632 nRet
= *maDataIter
++;
1634 nRet
|= *maDataIter
++;
1636 nRet
|= *maDataIter
++;
1638 nRet
|= *maDataIter
++;
1642 PNGReader::PNGReader(SvStream
& rIStream
) :
1643 mpImpl(new vcl::PNGReaderImpl(rIStream
))
1647 PNGReader::~PNGReader()
1651 BitmapEx
PNGReader::Read( const Size
& i_rPreviewSizeHint
)
1653 return mpImpl
->GetBitmapEx( i_rPreviewSizeHint
);
1656 const std::vector
< vcl::PNGReader::ChunkData
>& PNGReader::GetChunks() const
1658 return mpImpl
->GetAllChunks();
1661 void PNGReader::SetIgnoreGammaChunk(bool bIgnoreGammaChunk
)
1663 mpImpl
->SetIgnoreGammaChunk(bIgnoreGammaChunk
);
1668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */