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