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