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