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