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(); 193 void SetIgnoreGammaChunk( sal_Bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; }; 194 }; 195 196 // ------------------------------------------------------------------------------ 197 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 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 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 346 void PNGReaderImpl::ReadRemainingChunks() 347 { 348 while( ReadNextChunk() ) ; 349 } 350 351 // ------------------------------------------------------------------------ 352 353 // move position of mrPNGStream to the end of the file 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 377 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks() 378 { 379 ReadRemainingChunks(); 380 return maChunkSeq; 381 } 382 383 // ------------------------------------------------------------------------ 384 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1730 PNGReader::PNGReader( SvStream& rIStm ) : 1731 mpImpl( new ::vcl::PNGReaderImpl( rIStm ) ) 1732 { 1733 } 1734 1735 // ------------------------------------------------------------------------ 1736 1737 PNGReader::~PNGReader() 1738 { 1739 delete mpImpl; 1740 } 1741 1742 // ------------------------------------------------------------------------ 1743 1744 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint ) 1745 { 1746 return mpImpl->GetBitmapEx( i_rPreviewSizeHint ); 1747 } 1748 1749 // ------------------------------------------------------------------------ 1750 1751 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const 1752 { 1753 return mpImpl->GetAllChunks(); 1754 } 1755 1756 // ------------------------------------------------------------------------ 1757 1758 void PNGReader::SetIgnoreGammaChunk( sal_Bool b ) 1759 { 1760 mpImpl->SetIgnoreGammaChunk( b ); 1761 } 1762 1763 1764 } // namespace vcl 1765