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(); 192 void SetIgnoreGammaChunk( sal_Bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; }; 193 }; 194 195 // ------------------------------------------------------------------------------ 196 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 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 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 345 void PNGReaderImpl::ReadRemainingChunks() 346 { 347 while( ReadNextChunk() ) ; 348 } 349 350 // ------------------------------------------------------------------------ 351 352 // move position of mrPNGStream to the end of the file 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 376 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks() 377 { 378 ReadRemainingChunks(); 379 return maChunkSeq; 380 } 381 382 // ------------------------------------------------------------------------ 383 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1716 PNGReader::PNGReader( SvStream& rIStm ) : 1717 mpImpl( new ::vcl::PNGReaderImpl( rIStm ) ) 1718 { 1719 } 1720 1721 // ------------------------------------------------------------------------ 1722 1723 PNGReader::~PNGReader() 1724 { 1725 delete mpImpl; 1726 } 1727 1728 // ------------------------------------------------------------------------ 1729 1730 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint ) 1731 { 1732 return mpImpl->GetBitmapEx( i_rPreviewSizeHint ); 1733 } 1734 1735 // ------------------------------------------------------------------------ 1736 1737 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const 1738 { 1739 return mpImpl->GetAllChunks(); 1740 } 1741 1742 // ------------------------------------------------------------------------ 1743 1744 void PNGReader::SetIgnoreGammaChunk( sal_Bool b ) 1745 { 1746 mpImpl->SetIgnoreGammaChunk( b ); 1747 } 1748 1749 1750 } // namespace vcl 1751