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