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 bool ReadNextChunk(); 155 void ReadRemainingChunks(); 156 void SkipRemainingChunks(); 157 158 void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & ); 159 void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex ); 160 void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, sal_Bool bTrans ); 161 void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha ); 162 void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha ); 163 void ImplReadIDAT(); 164 bool ImplPreparePass(); 165 void ImplApplyFilter(); 166 void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ); 167 sal_Bool ImplReadTransparent(); 168 void ImplGetGamma(); 169 void ImplGetBackground(); 170 sal_uInt8 ImplScaleColor(); 171 sal_Bool ImplReadHeader( const Size& rPreviewSizeHint ); 172 sal_Bool ImplReadPalette(); 173 void ImplGetGrayPalette( sal_uInt16 ); 174 sal_uInt32 ImplReadsal_uInt32(); 175 176 public: 177 178 PNGReaderImpl( SvStream& ); 179 ~PNGReaderImpl(); 180 181 BitmapEx GetBitmapEx( const Size& rPreviewSizeHint ); 182 const std::vector< PNGReader::ChunkData >& GetAllChunks(); 183 void SetIgnoreGammaChunk( sal_Bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; }; 184 }; 185 186 // ------------------------------------------------------------------------------ 187 188 PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream ) 189 : mrPNGStream( rPNGStream ), 190 mpBmp ( NULL ), 191 mpAcc ( NULL ), 192 mpMaskBmp ( NULL ), 193 mpAlphaMask ( NULL ), 194 mpMaskAcc ( NULL ), 195 mpZCodec ( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ), 196 mpInflateInBuf ( NULL ), 197 mpScanPrior ( NULL ), 198 mpTransTab ( NULL ), 199 mpColorTable ( (sal_uInt8*) mpDefaultColorTable ), 200 mbzCodecInUse ( sal_False ), 201 mbStatus( sal_True), 202 mbIDAT( sal_False ), 203 mbGamma ( sal_False ), 204 mbpHYs ( sal_False ), 205 mbIgnoreGammaChunk ( sal_False ) 206 { 207 // prepare the PNG data stream 208 mnOrigStreamMode = mrPNGStream.GetNumberFormatInt(); 209 mrPNGStream.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); 210 211 // prepare the chunk reader 212 maChunkSeq.reserve( 16 ); 213 maChunkIter = maChunkSeq.begin(); 214 215 // estimate PNG file size (to allow sanity checks) 216 const sal_Size nStreamPos = mrPNGStream.Tell(); 217 mrPNGStream.Seek( STREAM_SEEK_TO_END ); 218 mnStreamSize = mrPNGStream.Tell(); 219 mrPNGStream.Seek( nStreamPos ); 220 221 // check the PNG header magic 222 sal_uInt32 nDummy = 0; 223 mrPNGStream >> nDummy; 224 mbStatus = (nDummy == 0x89504e47); 225 mrPNGStream >> nDummy; 226 mbStatus &= (nDummy == 0x0d0a1a0a); 227 228 mnPreviewShift = 0; 229 mnPreviewMask = (1 << mnPreviewShift) - 1; 230 } 231 232 // ------------------------------------------------------------------------ 233 234 PNGReaderImpl::~PNGReaderImpl() 235 { 236 mrPNGStream.SetNumberFormatInt( mnOrigStreamMode ); 237 238 if ( mbzCodecInUse ) 239 mpZCodec->EndCompression(); 240 241 if( mpColorTable != mpDefaultColorTable ) 242 delete[] mpColorTable; 243 244 delete mpBmp; 245 delete mpAlphaMask; 246 delete mpMaskBmp; 247 delete[] mpTransTab; 248 delete[] mpInflateInBuf; 249 delete[] mpScanPrior; 250 delete mpZCodec; 251 } 252 253 // ------------------------------------------------------------------------ 254 255 bool PNGReaderImpl::ReadNextChunk() 256 { 257 if( maChunkIter == maChunkSeq.end() ) 258 { 259 // get the next chunk from the stream 260 261 // unless we are at the end of the PNG stream 262 if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) ) 263 return false; 264 if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) ) 265 return false; 266 267 PNGReader::ChunkData aDummyChunk; 268 maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk ); 269 PNGReader::ChunkData& rChunkData = *maChunkIter; 270 271 // read the chunk header 272 mrPNGStream >> mnChunkLen >> mnChunkType; 273 rChunkData.nType = mnChunkType; 274 275 // #128377#/#149343# sanity check for chunk length 276 if( mnChunkLen < 0 ) 277 return false; 278 const sal_Size nStreamPos = mrPNGStream.Tell(); 279 if( nStreamPos + mnChunkLen >= mnStreamSize ) 280 return false; 281 282 // calculate chunktype CRC (swap it back to original byte order) 283 sal_uInt32 nChunkType = mnChunkType;; 284 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN) 285 nChunkType = SWAPLONG( nChunkType ); 286 #endif 287 sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 ); 288 289 // read the chunk data and check the CRC 290 if( mnChunkLen && !mrPNGStream.IsEof() ) 291 { 292 rChunkData.aData.resize( mnChunkLen ); 293 294 sal_Int32 nBytesRead = 0; 295 do { 296 sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ]; 297 nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead ); 298 } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) ); 299 300 nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen ); 301 maDataIter = rChunkData.aData.begin(); 302 } 303 sal_uInt32 nCheck; 304 mrPNGStream >> nCheck; 305 if( nCRC32 != nCheck ) 306 return false; 307 } 308 else 309 { 310 // the next chunk was already read 311 mnChunkType = (*maChunkIter).nType; 312 mnChunkLen = (*maChunkIter).aData.size(); 313 maDataIter = (*maChunkIter).aData.begin(); 314 } 315 316 ++maChunkIter; 317 if( mnChunkType == PNGCHUNK_IEND ) 318 return false; 319 return true; 320 } 321 322 // ------------------------------------------------------------------------ 323 324 // read the remaining chunks from mrPNGStream 325 void PNGReaderImpl::ReadRemainingChunks() 326 { 327 while( ReadNextChunk() ) ; 328 } 329 330 // ------------------------------------------------------------------------ 331 332 // move position of mrPNGStream to the end of the file 333 void PNGReaderImpl::SkipRemainingChunks() 334 { 335 // nothing to skip if the last chunk was read 336 if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) ) 337 return; 338 339 // read from the stream until the IEND chunk is found 340 const sal_Size nStreamPos = mrPNGStream.Tell(); 341 while( !mrPNGStream.IsEof() && (mrPNGStream.GetError() == ERRCODE_NONE) ) 342 { 343 mrPNGStream >> mnChunkLen >> mnChunkType; 344 if( mnChunkLen < 0 ) 345 break; 346 if( nStreamPos + mnChunkLen >= mnStreamSize ) 347 break; 348 mrPNGStream.SeekRel( mnChunkLen + 4 ); // skip data + CRC 349 if( mnChunkType == PNGCHUNK_IEND ) 350 break; 351 } 352 } 353 354 // ------------------------------------------------------------------------ 355 356 const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks() 357 { 358 ReadRemainingChunks(); 359 return maChunkSeq; 360 } 361 362 // ------------------------------------------------------------------------ 363 364 BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint ) 365 { 366 // reset to the first chunk 367 maChunkIter = maChunkSeq.begin(); 368 369 // parse the chunks 370 while( mbStatus && !mbIDAT && ReadNextChunk() ) 371 { 372 switch( mnChunkType ) 373 { 374 case PNGCHUNK_IHDR : 375 { 376 mbStatus = ImplReadHeader( rPreviewSizeHint ); 377 } 378 break; 379 380 case PNGCHUNK_gAMA : // the gamma chunk must precede 381 { // the 'IDAT' and also the 'PLTE'(if available ) 382 if ( !mbIgnoreGammaChunk && ( mbIDAT == sal_False ) ) 383 ImplGetGamma(); 384 } 385 break; 386 387 case PNGCHUNK_PLTE : 388 { 389 if ( !mbPalette ) 390 mbStatus = ImplReadPalette(); 391 } 392 break; 393 394 case PNGCHUNK_tRNS : 395 { 396 if ( !mbIDAT ) // the tRNS chunk must precede the IDAT 397 mbStatus = ImplReadTransparent(); 398 } 399 break; 400 401 case PNGCHUNK_bKGD : // the background chunk must appear 402 { 403 if ( ( mbIDAT == sal_False ) && mbPalette ) // before the 'IDAT' and after the 404 ImplGetBackground(); // PLTE(if available ) chunk. 405 } 406 break; 407 408 case PNGCHUNK_IDAT : 409 { 410 if ( !mpInflateInBuf ) // taking care that the header has properly been read 411 mbStatus = sal_False; 412 else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes 413 ImplReadIDAT(); 414 } 415 break; 416 417 case PNGCHUNK_pHYs : 418 { 419 if ( !mbIDAT && mnChunkLen == 9 ) 420 { 421 sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32(); 422 sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32(); 423 424 sal_uInt8 nUnitSpecifier = *maDataIter++; 425 if( (nUnitSpecifier == 1) && nXPixelPerMeter && nXPixelPerMeter ) 426 { 427 mbpHYs = sal_True; 428 429 // convert into MAP_100TH_MM 430 maPhysSize.Width() = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter ); 431 maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter ); 432 } 433 } 434 } 435 break; 436 437 case PNGCHUNK_IEND: 438 mbStatus = mbIDAT; // there is a problem if the image is not complete yet 439 break; 440 } 441 } 442 443 // release write access of the bitmaps 444 if ( mpAcc ) 445 mpBmp->ReleaseAccess( mpAcc ), mpAcc = NULL; 446 447 if ( mpMaskAcc ) 448 { 449 if ( mpAlphaMask ) 450 mpAlphaMask->ReleaseAccess( mpMaskAcc ); 451 else if ( mpMaskBmp ) 452 mpMaskBmp->ReleaseAccess( mpMaskAcc ); 453 454 mpMaskAcc = NULL; 455 } 456 457 // return the resulting BitmapEx 458 BitmapEx aRet; 459 460 if( !mbStatus || !mbIDAT ) 461 aRet.Clear(); 462 else 463 { 464 if ( mpAlphaMask ) 465 aRet = BitmapEx( *mpBmp, *mpAlphaMask ); 466 else if ( mpMaskBmp ) 467 aRet = BitmapEx( *mpBmp, *mpMaskBmp ); 468 else 469 aRet = *mpBmp; 470 471 if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() ) 472 { 473 aRet.SetPrefMapMode( MAP_100TH_MM ); 474 aRet.SetPrefSize( maPhysSize ); 475 } 476 477 #if 0 478 // TODO: make sure nobody depends on the stream being after the IEND chunks 479 // => let them do ReadChunks before 480 ReadRemainingChunks(); 481 #endif 482 } 483 484 return aRet; 485 } 486 487 // ------------------------------------------------------------------------ 488 489 sal_Bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) 490 { 491 if( mnChunkLen < 13 ) 492 return sal_False; 493 494 maOrigSize.Width() = ImplReadsal_uInt32(); 495 maOrigSize.Height() = ImplReadsal_uInt32(); 496 497 if ( !maOrigSize.Width() || !maOrigSize.Height() ) 498 return sal_False; 499 500 mnPngDepth = *(maDataIter++); 501 mnColorType = *(maDataIter++); 502 503 mnCompressionType = *(maDataIter++); 504 if( mnCompressionType != 0 ) // unknown compression type 505 return sal_False; 506 507 mnFilterType = *(maDataIter++); 508 if( mnFilterType != 0 ) // unknown filter type 509 return sal_False; 510 511 mnInterlaceType = *(maDataIter++); 512 switch ( mnInterlaceType ) // filter type valid ? 513 { 514 case 0 : // progressive image 515 mnPass = 7; 516 break; 517 case 1 : // Adam7-interlaced image 518 mnPass = 0; 519 break; 520 default: 521 return sal_False; 522 } 523 524 mbPalette = sal_True; 525 mbIDAT = mbAlphaChannel = mbTransparent = sal_False; 526 mbGrayScale = mbRGBTriple = sal_False; 527 mnTargetDepth = mnPngDepth; 528 sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3; 529 530 // valid color types are 0,2,3,4 & 6 531 switch ( mnColorType ) 532 { 533 case 0 : // each pixel is a grayscale 534 { 535 switch ( mnPngDepth ) 536 { 537 case 2 : // 2bit target not available -> use four bits 538 mnTargetDepth = 4; // we have to expand the bitmap 539 mbGrayScale = sal_True; 540 break; 541 case 16 : 542 mnTargetDepth = 8; // we have to reduce the bitmap 543 // fall through 544 case 1 : 545 case 4 : 546 case 8 : 547 mbGrayScale = sal_True; 548 break; 549 default : 550 return sal_False; 551 } 552 } 553 break; 554 555 case 2 : // each pixel is an RGB triple 556 { 557 mbRGBTriple = sal_True; 558 nScansize64 *= 3; 559 switch ( mnPngDepth ) 560 { 561 case 16 : // we have to reduce the bitmap 562 case 8 : 563 mnTargetDepth = 24; 564 break; 565 default : 566 return sal_False; 567 } 568 } 569 break; 570 571 case 3 : // each pixel is a palette index 572 { 573 switch ( mnPngDepth ) 574 { 575 case 2 : 576 mnTargetDepth = 4; // we have to expand the bitmap 577 // fall through 578 case 1 : 579 case 4 : 580 case 8 : 581 mbPalette = sal_False; 582 break; 583 default : 584 return sal_False; 585 } 586 } 587 break; 588 589 case 4 : // each pixel is a grayscale sample followed by an alpha sample 590 { 591 nScansize64 *= 2; 592 mbAlphaChannel = sal_True; 593 switch ( mnPngDepth ) 594 { 595 case 16 : 596 mnTargetDepth = 8; // we have to reduce the bitmap 597 case 8 : 598 mbGrayScale = sal_True; 599 break; 600 default : 601 return sal_False; 602 } 603 } 604 break; 605 606 case 6 : // each pixel is an RGB triple followed by an alpha sample 607 { 608 mbRGBTriple = sal_True; 609 nScansize64 *= 4; 610 mbAlphaChannel = sal_True; 611 switch (mnPngDepth ) 612 { 613 case 16 : // we have to reduce the bitmap 614 case 8 : 615 mnTargetDepth = 24; 616 break; 617 default : 618 return sal_False; 619 } 620 } 621 break; 622 623 default : 624 return sal_False; 625 } 626 627 mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() ); 628 if ( !mnBPP ) 629 mnBPP = 1; 630 631 nScansize64++; // each scanline includes one filterbyte 632 633 if ( nScansize64 > SAL_MAX_UINT32 ) 634 return sal_False; 635 636 mnScansize = static_cast< sal_uInt32 >( nScansize64 ); 637 638 // TODO: switch between both scanlines instead of copying 639 mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ]; 640 mpScanCurrent = mpInflateInBuf; 641 mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ]; 642 643 if ( !mpInflateInBuf || !mpScanPrior ) 644 return sal_False; 645 646 // calculate target size from original size and the preview hint 647 if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() ) 648 { 649 Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() ); 650 maTargetSize = maOrigSize; 651 652 if( aPreviewSize.Width() == 0 ) { 653 aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() ); 654 if( aPreviewSize.Width() <= 0 ) 655 aPreviewSize.setWidth( 1 ); 656 } else if( aPreviewSize.Height() == 0 ) { 657 aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() ); 658 if( aPreviewSize.Height() <= 0 ) 659 aPreviewSize.setHeight( 1 ); 660 } 661 662 if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) { 663 OSL_TRACE("preview size %dx%d", aPreviewSize.Width(), aPreviewSize.Height() ); 664 665 for( int i = 1; i < 5; ++i ) 666 { 667 if( (maTargetSize.Width() >> i) < aPreviewSize.Width() ) 668 break; 669 if( (maTargetSize.Height() >> i) < aPreviewSize.Height() ) 670 break; 671 mnPreviewShift = i; 672 } 673 mnPreviewMask = (1 << mnPreviewShift) - 1; 674 } 675 } 676 677 maTargetSize.Width() = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift; 678 maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift; 679 680 mpBmp = new Bitmap( maTargetSize, mnTargetDepth ); 681 mpAcc = mpBmp->AcquireWriteAccess(); 682 if( !mpAcc ) 683 return sal_False; 684 685 mpBmp->SetSourceSizePixel( maOrigSize ); 686 687 if ( mbAlphaChannel ) 688 { 689 mpAlphaMask = new AlphaMask( maTargetSize ); 690 mpAlphaMask->Erase( 128 ); 691 mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); 692 if( !mpMaskAcc ) 693 return sal_False; 694 } 695 696 if ( mbGrayScale ) 697 ImplGetGrayPalette( mnPngDepth ); 698 699 ImplPreparePass(); 700 701 return sal_True; 702 } 703 704 // ------------------------------------------------------------------------ 705 706 void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth ) 707 { 708 if( nBitDepth > 8 ) 709 nBitDepth = 8; 710 711 sal_uInt16 nPaletteEntryCount = 1 << nBitDepth; 712 sal_uInt32 nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0; 713 714 // no bitdepth==2 available 715 // but bitdepth==4 with two unused bits is close enough 716 if( nBitDepth == 2 ) 717 nPaletteEntryCount = 16; 718 719 mpAcc->SetPaletteEntryCount( nPaletteEntryCount ); 720 for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd ) 721 mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ], 722 mpColorTable[ nStart ], mpColorTable[ nStart ] ) ); 723 } 724 725 // ------------------------------------------------------------------------ 726 727 sal_Bool PNGReaderImpl::ImplReadPalette() 728 { 729 sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 ); 730 731 if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc ) 732 { 733 mbPalette = sal_True; 734 mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount ); 735 736 for ( sal_uInt16 i = 0; i < nCount; i++ ) 737 { 738 sal_uInt8 nRed = mpColorTable[ *maDataIter++ ]; 739 sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ]; 740 sal_uInt8 nBlue = mpColorTable[ *maDataIter++ ]; 741 mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) ); 742 } 743 } 744 else 745 mbStatus = sal_False; 746 747 return mbStatus; 748 } 749 750 // ------------------------------------------------------------------------ 751 752 sal_Bool PNGReaderImpl::ImplReadTransparent() 753 { 754 bool bNeedAlpha = false; 755 756 if ( mpTransTab == NULL ) 757 { 758 switch ( mnColorType ) 759 { 760 case 0 : 761 { 762 if ( mnChunkLen == 2 ) 763 { 764 mpTransTab = new sal_uInt8[ 256 ]; 765 rtl_fillMemory( mpTransTab, 256, 0xff ); 766 // color type 0 and 4 is always greyscale, 767 // so the return value can be used as index 768 sal_uInt8 nIndex = ImplScaleColor(); 769 mpTransTab[ nIndex ] = 0; 770 mbTransparent = true; 771 } 772 } 773 break; 774 775 case 2 : 776 { 777 if ( mnChunkLen == 6 ) 778 { 779 mnTransRed = ImplScaleColor(); 780 mnTransGreen = ImplScaleColor(); 781 mnTransBlue = ImplScaleColor(); 782 mbTransparent = true; 783 } 784 } 785 break; 786 787 case 3 : 788 { 789 if ( mnChunkLen <= 256 ) 790 { 791 mpTransTab = new sal_uInt8 [ 256 ]; 792 rtl_fillMemory( mpTransTab, 256, 0xff ); 793 rtl_copyMemory( mpTransTab, &(*maDataIter), mnChunkLen ); 794 maDataIter += mnChunkLen; 795 mbTransparent = true; 796 // need alpha transparency if not on/off masking 797 for( int i = 0; i < mnChunkLen; ++i ) 798 bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF); 799 } 800 } 801 break; 802 } 803 } 804 805 if( mbTransparent && !mbAlphaChannel && !mpMaskBmp ) 806 { 807 if( bNeedAlpha) 808 { 809 mpAlphaMask = new AlphaMask( maTargetSize ); 810 mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); 811 } 812 else 813 { 814 mpMaskBmp = new Bitmap( maTargetSize, 1 ); 815 mpMaskAcc = mpMaskBmp->AcquireWriteAccess(); 816 } 817 mbTransparent = (mpMaskAcc != NULL); 818 if( !mbTransparent ) 819 return sal_False; 820 mcOpaqueColor = BitmapColor( 0x00 ); 821 mcTranspColor = BitmapColor( 0xFF ); 822 mpMaskAcc->Erase( 0x00 ); 823 } 824 825 return sal_True; 826 } 827 828 // ------------------------------------------------------------------------ 829 830 void PNGReaderImpl::ImplGetGamma() 831 { 832 if( mnChunkLen < 4 ) 833 return; 834 835 sal_uInt32 nGammaValue = ImplReadsal_uInt32(); 836 double fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) ); 837 double fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma ); 838 839 if ( fInvGamma != 1.0 ) 840 { 841 mbGamma = sal_True; 842 843 if ( mpColorTable == mpDefaultColorTable ) 844 mpColorTable = new sal_uInt8[ 256 ]; 845 846 for ( sal_Int32 i = 0; i < 256; i++ ) 847 mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5); 848 849 if ( mbGrayScale ) 850 ImplGetGrayPalette( mnPngDepth ); 851 } 852 } 853 854 // ------------------------------------------------------------------------ 855 856 void PNGReaderImpl::ImplGetBackground() 857 { 858 switch ( mnColorType ) 859 { 860 case 3 : 861 { 862 if ( mnChunkLen == 1 ) 863 { 864 sal_uInt16 nCol = *maDataIter++; 865 if ( nCol < mpAcc->GetPaletteEntryCount() ) 866 { 867 mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) ); 868 break; 869 } 870 } 871 } 872 break; 873 874 case 0 : 875 case 4 : 876 { 877 if ( mnChunkLen == 2 ) 878 { 879 // the color type 0 and 4 is always greyscale, 880 // so the return value can be used as index 881 sal_uInt8 nIndex = ImplScaleColor(); 882 mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) ); 883 } 884 } 885 break; 886 887 case 2 : 888 case 6 : 889 { 890 if ( mnChunkLen == 6 ) 891 { 892 sal_uInt8 nRed = ImplScaleColor(); 893 sal_uInt8 nGreen = ImplScaleColor(); 894 sal_uInt8 nBlue = ImplScaleColor(); 895 mpAcc->Erase( Color( nRed, nGreen, nBlue ) ); 896 } 897 } 898 break; 899 } 900 } 901 902 // ------------------------------------------------------------------------ 903 904 // for color type 0 and 4 (greyscale) the return value is always index to the color 905 // 2 and 6 (RGB) the return value is always the 8 bit color component 906 sal_uInt8 PNGReaderImpl::ImplScaleColor() 907 { 908 sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 ); 909 sal_uInt16 nCol = ( *maDataIter++ << 8 ); 910 911 nCol += *maDataIter++ & (sal_uInt16)nMask; 912 913 if ( mnPngDepth > 8 ) // convert 16bit graphics to 8 914 nCol >>= 8; 915 916 return (sal_uInt8) nCol; 917 } 918 919 // ------------------------------------------------------------------------ 920 // ImplReadIDAT reads as much image data as needed 921 922 void PNGReaderImpl::ImplReadIDAT() 923 { 924 if( mnChunkLen > 0 ) 925 { 926 if ( mbzCodecInUse == sal_False ) 927 { 928 mbzCodecInUse = sal_True; 929 mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT ); 930 } 931 mpZCodec->SetBreak( mnChunkLen ); 932 SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, STREAM_READ ); 933 934 while ( ( mpZCodec->GetBreak() ) ) 935 { 936 // get bytes needed to fill the current scanline 937 sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf); 938 sal_Int32 nRead = mpZCodec->ReadAsynchron( aIStrm, mpScanCurrent, nToRead ); 939 if ( nRead < 0 ) 940 { 941 mbStatus = sal_False; 942 break; 943 } 944 if ( nRead < nToRead ) 945 { 946 mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk 947 break; 948 } 949 else // this scanline is Finished 950 { 951 mpScanCurrent = mpInflateInBuf; 952 ImplApplyFilter(); 953 954 ImplDrawScanline( mnXStart, mnXAdd ); 955 mnYpos += mnYAdd; 956 } 957 958 if ( mnYpos >= (sal_uInt32)maOrigSize.Height() ) 959 { 960 if( (mnPass < 7) && mnInterlaceType ) 961 if( ImplPreparePass() ) 962 continue; 963 mbIDAT = true; 964 break; 965 } 966 } 967 } 968 969 if( mbIDAT ) 970 { 971 mpZCodec->EndCompression(); 972 mbzCodecInUse = sal_False; 973 } 974 } 975 976 // --------------------------------------------------------------------------------------------------- 977 978 bool PNGReaderImpl::ImplPreparePass() 979 { 980 struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; }; 981 static const InterlaceParams aInterlaceParams[8] = 982 { 983 // non-interlaced 984 { 0, 0, 1, 1 }, 985 // Adam7-interlaced 986 { 0, 0, 8, 8 }, // pass 1 987 { 4, 0, 8, 8 }, // pass 2 988 { 0, 4, 4, 8 }, // pass 3 989 { 2, 0, 4, 4 }, // pass 4 990 { 0, 2, 2, 4 }, // pass 5 991 { 1, 0, 2, 2 }, // pass 6 992 { 0, 1, 1, 2 } // pass 7 993 }; 994 995 const InterlaceParams* pParam = &aInterlaceParams[ 0 ]; 996 if( mnInterlaceType ) 997 { 998 while( ++mnPass <= 7 ) 999 { 1000 pParam = &aInterlaceParams[ mnPass ]; 1001 1002 // skip this pass if the original image is too small for it 1003 if( (pParam->mnXStart < maOrigSize.Width()) 1004 && (pParam->mnYStart < maOrigSize.Height()) ) 1005 break; 1006 } 1007 if( mnPass > 7 ) 1008 return false; 1009 1010 // skip the last passes if possible (for scaled down target images) 1011 if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) ) 1012 return false; 1013 } 1014 1015 mnYpos = pParam->mnYStart; 1016 mnXStart = pParam->mnXStart; 1017 mnXAdd = pParam->mnXAdd; 1018 mnYAdd = pParam->mnYAdd; 1019 1020 // in Interlace mode the size of scanline is not constant 1021 // so first we calculate the number of entrys 1022 long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd; 1023 mnScansize = nScanWidth; 1024 1025 if( mbRGBTriple ) 1026 mnScansize = 3 * nScanWidth; 1027 1028 if( mbAlphaChannel ) 1029 mnScansize += nScanWidth; 1030 1031 // convert to width in bytes 1032 mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3; 1033 1034 ++mnScansize; // scan size also needs room for the filtertype byte 1035 rtl_zeroMemory( mpScanPrior, mnScansize ); 1036 1037 return true; 1038 } 1039 1040 // ---------------------------------------------------------------------------- 1041 // ImplApplyFilter writes the complete Scanline (nY) 1042 // in interlace mode the parameter nXStart and nXAdd are non-zero 1043 1044 void PNGReaderImpl::ImplApplyFilter() 1045 { 1046 OSL_ASSERT( mnScansize >= mnBPP + 1 ); 1047 const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize; 1048 1049 sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline 1050 switch ( nFilterType ) 1051 { 1052 default: // unknown Scanline Filter Type 1053 case 0: // Filter Type "None" 1054 // we let the pixels pass and display the data unfiltered 1055 break; 1056 1057 case 1: // Scanline Filter Type "Sub" 1058 { 1059 sal_uInt8* p1 = mpInflateInBuf + 1; 1060 const sal_uInt8* p2 = p1; 1061 p1 += mnBPP; 1062 1063 // use left pixels 1064 do 1065 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); 1066 while( ++p1 < pScanEnd ); 1067 } 1068 break; 1069 1070 case 2: // Scanline Filter Type "Up" 1071 { 1072 sal_uInt8* p1 = mpInflateInBuf + 1; 1073 const sal_uInt8* p2 = mpScanPrior + 1; 1074 1075 // use pixels from prior line 1076 while( p1 < pScanEnd ) 1077 { 1078 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); 1079 ++p1; 1080 } 1081 } 1082 break; 1083 1084 case 3: // Scanline Filter Type "Average" 1085 { 1086 sal_uInt8* p1 = mpInflateInBuf + 1; 1087 const sal_uInt8* p2 = mpScanPrior + 1; 1088 const sal_uInt8* p3 = p1; 1089 1090 // use one pixel from prior line 1091 for( int n = mnBPP; --n >= 0; ++p1, ++p2) 1092 *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) ); 1093 1094 // predict by averaging the left and prior line pixels 1095 while( p1 < pScanEnd ) 1096 { 1097 *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) ); 1098 ++p1; 1099 } 1100 } 1101 break; 1102 1103 case 4: // Scanline Filter Type "PaethPredictor" 1104 { 1105 sal_uInt8* p1 = mpInflateInBuf + 1; 1106 const sal_uInt8* p2 = mpScanPrior + 1; 1107 const sal_uInt8* p3 = p1; 1108 const sal_uInt8* p4 = p2; 1109 1110 // use one pixel from prior line 1111 for( int n = mnBPP; --n >= 0; ++p1) 1112 *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); 1113 1114 // predict by using the left and the prior line pixels 1115 while( p1 < pScanEnd ) 1116 { 1117 int na = *(p2++); 1118 int nb = *(p3++); 1119 int nc = *(p4++); 1120 1121 int npa = nb - (int)nc; 1122 int npb = na - (int)nc; 1123 int npc = npa + npb; 1124 1125 if( npa < 0 ) 1126 npa =-npa; 1127 if( npb < 0 ) 1128 npb =-npb; 1129 if( npc < 0 ) 1130 npc =-npc; 1131 1132 if( npa > npb ) 1133 na = nb, npa = npb; 1134 if( npa > npc ) 1135 na = nc; 1136 1137 *p1 = static_cast<sal_uInt8>( *p1 + na ); 1138 ++p1; 1139 } 1140 } 1141 break; 1142 } 1143 1144 rtl_copyMemory( mpScanPrior, mpInflateInBuf, mnScansize ); 1145 } 1146 1147 // --------------------------------------------------------------------------------------------------- 1148 // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap 1149 // In interlace mode the parameter nXStart and nXAdd append to the currently used pass 1150 1151 void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ) 1152 { 1153 // optimization for downscaling 1154 if( mnYpos & mnPreviewMask ) 1155 return; 1156 if( nXStart & mnPreviewMask ) 1157 return; 1158 1159 // convert nY to pixel units in the target image 1160 // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods 1161 const sal_uInt32 nY = mnYpos >> mnPreviewShift; 1162 1163 const sal_uInt8* pTmp = mpInflateInBuf + 1; 1164 if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries 1165 { 1166 switch ( mpAcc->GetBitCount() ) 1167 { 1168 case 1 : 1169 { 1170 if ( mbTransparent ) 1171 { 1172 for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd ) 1173 { 1174 sal_uInt8 nCol; 1175 nShift = (nShift - 1) & 7; 1176 if ( nShift == 0 ) 1177 nCol = *(pTmp++); 1178 else 1179 nCol = static_cast<sal_uInt8>( *pTmp >> nShift ); 1180 nCol &= 1; 1181 1182 ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] ); 1183 } 1184 } 1185 else 1186 { // BMP_FORMAT_1BIT_MSB_PAL 1187 for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd ) 1188 { 1189 nShift = (nShift - 1) & 7; 1190 1191 sal_uInt8 nCol; 1192 if ( nShift == 0 ) 1193 nCol = *(pTmp++); 1194 else 1195 nCol = static_cast<sal_uInt8>( *pTmp >> nShift ); 1196 nCol &= 1; 1197 1198 ImplSetPixel( nY, nX, nCol ); 1199 } 1200 } 1201 } 1202 break; 1203 1204 case 4 : 1205 { 1206 if ( mbTransparent ) 1207 { 1208 if ( mnPngDepth == 4 ) // check if source has a two bit pixel format 1209 { 1210 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex ) 1211 { 1212 if( nXIndex & 1 ) 1213 { 1214 ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] ); 1215 pTmp++; 1216 } 1217 else 1218 { 1219 ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] ); 1220 } 1221 } 1222 } 1223 else // if ( mnPngDepth == 2 ) 1224 { 1225 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) 1226 { 1227 sal_uInt8 nCol; 1228 switch( nXIndex & 3 ) 1229 { 1230 case 0 : 1231 nCol = *pTmp >> 6; 1232 break; 1233 1234 case 1 : 1235 nCol = ( *pTmp >> 4 ) & 0x03 ; 1236 break; 1237 1238 case 2 : 1239 nCol = ( *pTmp >> 2 ) & 0x03; 1240 break; 1241 1242 case 3 : 1243 nCol = ( *pTmp++ ) & 0x03; 1244 break; 1245 1246 default: // get rid of nCol uninitialized warning 1247 nCol = 0; 1248 break; 1249 } 1250 1251 ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] ); 1252 } 1253 } 1254 } 1255 else 1256 { 1257 if ( mnPngDepth == 4 ) // maybe the source is a two bitmap graphic 1258 { // BMP_FORMAT_4BIT_LSN_PAL 1259 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) 1260 { 1261 if( nXIndex & 1 ) 1262 ImplSetPixel( nY, nX, *pTmp++ & 0x0f ); 1263 else 1264 ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f ); 1265 } 1266 } 1267 else // if ( mnPngDepth == 2 ) 1268 { 1269 for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) 1270 { 1271 switch( nXIndex & 3 ) 1272 { 1273 case 0 : 1274 ImplSetPixel( nY, nX, *pTmp >> 6 ); 1275 break; 1276 1277 case 1 : 1278 ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 ); 1279 break; 1280 1281 case 2 : 1282 ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 ); 1283 break; 1284 1285 case 3 : 1286 ImplSetPixel( nY, nX, *pTmp++ & 0x03 ); 1287 break; 1288 } 1289 } 1290 } 1291 } 1292 } 1293 break; 1294 1295 case 8 : 1296 { 1297 if ( mbAlphaChannel ) 1298 { 1299 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale 1300 { 1301 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) 1302 ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] ); 1303 } 1304 else 1305 { 1306 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) 1307 ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] ); 1308 } 1309 } 1310 else if ( mbTransparent ) 1311 { 1312 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale 1313 { 1314 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ ) 1315 ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] ); 1316 } 1317 else 1318 { 1319 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) 1320 ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] ); 1321 } 1322 } 1323 else // neither alpha nor transparency 1324 { 1325 if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale 1326 { 1327 if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible 1328 { 1329 int nLineBytes = maOrigSize.Width(); 1330 mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes ); 1331 pTmp += nLineBytes; 1332 } 1333 else 1334 { 1335 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd ) 1336 ImplSetPixel( nY, nX, *pTmp++ ); 1337 } 1338 } 1339 else 1340 { 1341 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) 1342 ImplSetPixel( nY, nX, *pTmp ); 1343 } 1344 } 1345 } 1346 break; 1347 1348 default : 1349 mbStatus = sal_False; 1350 break; 1351 } 1352 } 1353 else // no palette => truecolor 1354 { 1355 if( mbAlphaChannel ) // has RGB + alpha 1356 { // BMP_FORMAT_32BIT_TC_RGBA 1357 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample 1358 { 1359 if ( mpColorTable != mpDefaultColorTable ) 1360 { 1361 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) 1362 ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], 1363 mpColorTable[ pTmp[ 1 ] ], 1364 mpColorTable[ pTmp[ 2 ] ] ), pTmp[ 3 ] ); 1365 } 1366 else 1367 { 1368 // if ( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible 1369 // { 1370 // int nLineBytes = 4 * maOrigSize.Width(); 1371 // mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_32BIT_TC_RGBA, nLineBytes ); 1372 // pTmp += nLineBytes; 1373 // } 1374 // else 1375 { 1376 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) 1377 ImplSetAlphaPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ), pTmp[3] ); 1378 } 1379 } 1380 } 1381 else 1382 { // BMP_FORMAT_64BIT_TC_RGBA 1383 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 ) 1384 ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], 1385 mpColorTable[ pTmp[ 2 ] ], 1386 mpColorTable[ pTmp[ 4 ] ] ), pTmp[6] ); 1387 } 1388 } 1389 else if( mbTransparent ) // has RGB + transparency 1390 { // BMP_FORMAT_24BIT_TC_RGB 1391 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample 1392 { 1393 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) 1394 { 1395 sal_uInt8 nRed = pTmp[ 0 ]; 1396 sal_uInt8 nGreen = pTmp[ 1 ]; 1397 sal_uInt8 nBlue = pTmp[ 2 ]; 1398 sal_Bool bTransparent = ( ( nRed == mnTransRed ) 1399 && ( nGreen == mnTransGreen ) 1400 && ( nBlue == mnTransBlue ) ); 1401 1402 ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ], 1403 mpColorTable[ nGreen ], 1404 mpColorTable[ nBlue ] ), bTransparent ); 1405 } 1406 } 1407 else 1408 { // BMP_FORMAT_48BIT_TC_RGB 1409 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 ) 1410 { 1411 sal_uInt8 nRed = pTmp[ 0 ]; 1412 sal_uInt8 nGreen = pTmp[ 2 ]; 1413 sal_uInt8 nBlue = pTmp[ 4 ]; 1414 sal_Bool bTransparent = ( ( nRed == mnTransRed ) 1415 && ( nGreen == mnTransGreen ) 1416 && ( nBlue == mnTransBlue ) ); 1417 1418 ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ], 1419 mpColorTable[ nGreen ], 1420 mpColorTable[ nBlue ] ), bTransparent ); 1421 } 1422 } 1423 } 1424 else // has RGB but neither alpha nor transparency 1425 { // BMP_FORMAT_24BIT_TC_RGB 1426 if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample 1427 { 1428 if ( mpColorTable != mpDefaultColorTable ) 1429 { 1430 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) 1431 ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], 1432 mpColorTable[ pTmp[ 1 ] ], 1433 mpColorTable[ pTmp[ 2 ] ] ) ); 1434 } 1435 else 1436 { 1437 if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible 1438 { 1439 int nLineBytes = maOrigSize.Width() * 3; 1440 mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_24BIT_TC_RGB, nLineBytes ); 1441 pTmp += nLineBytes; 1442 } 1443 else 1444 { 1445 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) 1446 ImplSetPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ) ); 1447 } 1448 } 1449 } 1450 else 1451 { // BMP_FORMAT_48BIT_TC_RGB 1452 for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 ) 1453 ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], 1454 mpColorTable[ pTmp[ 2 ] ], 1455 mpColorTable[ pTmp[ 4 ] ] ) ); 1456 } 1457 } 1458 } 1459 } 1460 1461 // ------------------------------------------------------------------------ 1462 1463 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor ) 1464 { 1465 // TODO: get preview mode checks out of inner loop 1466 if( nX & mnPreviewMask ) 1467 return; 1468 nX >>= mnPreviewShift; 1469 1470 mpAcc->SetPixel( nY, nX, rBitmapColor ); 1471 } 1472 1473 // ------------------------------------------------------------------------ 1474 1475 void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex ) 1476 { 1477 // TODO: get preview mode checks out of inner loop 1478 if( nX & mnPreviewMask ) 1479 return; 1480 nX >>= mnPreviewShift; 1481 1482 mpAcc->SetPixel( nY, nX, nPalIndex ); 1483 } 1484 1485 // ------------------------------------------------------------------------ 1486 1487 void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, sal_Bool bTrans ) 1488 { 1489 // TODO: get preview mode checks out of inner loop 1490 if( nX & mnPreviewMask ) 1491 return; 1492 nX >>= mnPreviewShift; 1493 1494 mpAcc->SetPixel( nY, nX, rBitmapColor ); 1495 1496 if ( bTrans ) 1497 mpMaskAcc->SetPixel( nY, nX, mcTranspColor ); 1498 else 1499 mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor ); 1500 } 1501 1502 // ------------------------------------------------------------------------ 1503 1504 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, 1505 sal_uInt8 nPalIndex, sal_uInt8 nAlpha ) 1506 { 1507 // TODO: get preview mode checks out of inner loop 1508 if( nX & mnPreviewMask ) 1509 return; 1510 nX >>= mnPreviewShift; 1511 1512 mpAcc->SetPixel( nY, nX, nPalIndex ); 1513 mpMaskAcc->SetPixel( nY, nX, ~nAlpha ); 1514 } 1515 1516 // ------------------------------------------------------------------------ 1517 1518 void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, 1519 const BitmapColor& rBitmapColor, sal_uInt8 nAlpha ) 1520 { 1521 // TODO: get preview mode checks out of inner loop 1522 if( nX & mnPreviewMask ) 1523 return; 1524 nX >>= mnPreviewShift; 1525 1526 mpAcc->SetPixel( nY, nX, rBitmapColor ); 1527 mpMaskAcc->SetPixel( nY, nX, ~nAlpha ); 1528 } 1529 1530 // ------------------------------------------------------------------------ 1531 1532 sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32() 1533 { 1534 sal_uInt32 nRet; 1535 nRet = *maDataIter++; 1536 nRet <<= 8; 1537 nRet |= *maDataIter++; 1538 nRet <<= 8; 1539 nRet |= *maDataIter++; 1540 nRet <<= 8; 1541 nRet |= *maDataIter++; 1542 return nRet; 1543 } 1544 1545 // ------------------------------------------------------------------------ 1546 1547 // ------------- 1548 // - PNGReader - 1549 // ------------- 1550 1551 PNGReader::PNGReader( SvStream& rIStm ) : 1552 mpImpl( new ::vcl::PNGReaderImpl( rIStm ) ) 1553 { 1554 } 1555 1556 // ------------------------------------------------------------------------ 1557 1558 PNGReader::~PNGReader() 1559 { 1560 delete mpImpl; 1561 } 1562 1563 // ------------------------------------------------------------------------ 1564 1565 BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint ) 1566 { 1567 return mpImpl->GetBitmapEx( i_rPreviewSizeHint ); 1568 } 1569 1570 // ------------------------------------------------------------------------ 1571 1572 const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const 1573 { 1574 return mpImpl->GetAllChunks(); 1575 } 1576 1577 // ------------------------------------------------------------------------ 1578 1579 void PNGReader::SetIgnoreGammaChunk( sal_Bool b ) 1580 { 1581 mpImpl->SetIgnoreGammaChunk( b ); 1582 } 1583 1584 1585 } // namespace vcl 1586