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