nsPNGDecoder.cpp (40328B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ImageLogging.h" // Must appear first 8 #include "nsPNGDecoder.h" 9 10 #include <algorithm> 11 #include <cstdint> 12 13 #include "EXIF.h" 14 #include "gfxColor.h" 15 #include "gfxPlatform.h" 16 #include "imgFrame.h" 17 #include "nsColor.h" 18 #include "nsRect.h" 19 #include "nspr.h" 20 #include "png.h" 21 22 #include "RasterImage.h" 23 #include "SurfaceCache.h" 24 #include "SurfacePipeFactory.h" 25 #include "mozilla/DebugOnly.h" 26 27 using namespace mozilla::gfx; 28 29 using std::min; 30 31 namespace mozilla { 32 namespace image { 33 34 static LazyLogModule sPNGLog("PNGDecoder"); 35 static LazyLogModule sPNGDecoderAccountingLog("PNGDecoderAccounting"); 36 37 // limit image dimensions (bug #251381, #591822, #967656, and #1283961) 38 #ifndef MOZ_PNG_MAX_WIDTH 39 # define MOZ_PNG_MAX_WIDTH 0x7fffffff // Unlimited 40 #endif 41 #ifndef MOZ_PNG_MAX_HEIGHT 42 # define MOZ_PNG_MAX_HEIGHT 0x7fffffff // Unlimited 43 #endif 44 45 /* Controls the maximum chunk size configuration for libpng. We set this to a 46 * very large number, 256MB specifically. */ 47 static constexpr png_alloc_size_t kPngMaxChunkSize = 0x10000000; 48 49 nsPNGDecoder::AnimFrameInfo::AnimFrameInfo() 50 : mDispose(DisposalMethod::KEEP), mBlend(BlendMethod::OVER), mTimeout(0) {} 51 52 #ifdef PNG_APNG_SUPPORTED 53 54 int32_t GetNextFrameDelay(png_structp aPNG, png_infop aInfo) { 55 // Delay, in seconds, is delayNum / delayDen. 56 png_uint_16 delayNum = png_get_next_frame_delay_num(aPNG, aInfo); 57 png_uint_16 delayDen = png_get_next_frame_delay_den(aPNG, aInfo); 58 59 if (delayNum == 0) { 60 return 0; // SetFrameTimeout() will set to a minimum. 61 } 62 63 if (delayDen == 0) { 64 delayDen = 100; // So says the APNG spec. 65 } 66 67 // Need to cast delay_num to float to have a proper division and 68 // the result to int to avoid a compiler warning. 69 return static_cast<int32_t>(static_cast<double>(delayNum) * 1000 / delayDen); 70 } 71 72 nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo) 73 : mDispose(DisposalMethod::KEEP), mBlend(BlendMethod::OVER), mTimeout(0) { 74 png_byte dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo); 75 png_byte blend_op = png_get_next_frame_blend_op(aPNG, aInfo); 76 77 if (dispose_op == PNG_DISPOSE_OP_PREVIOUS) { 78 mDispose = DisposalMethod::RESTORE_PREVIOUS; 79 } else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND) { 80 mDispose = DisposalMethod::CLEAR; 81 } else { 82 mDispose = DisposalMethod::KEEP; 83 } 84 85 if (blend_op == PNG_BLEND_OP_SOURCE) { 86 mBlend = BlendMethod::SOURCE; 87 } else { 88 mBlend = BlendMethod::OVER; 89 } 90 91 mTimeout = GetNextFrameDelay(aPNG, aInfo); 92 } 93 #endif 94 95 // First 8 bytes of a PNG file 96 const uint8_t nsPNGDecoder::pngSignatureBytes[] = {137, 80, 78, 71, 97 13, 10, 26, 10}; 98 99 nsPNGDecoder::nsPNGDecoder(RasterImage* aImage) 100 : Decoder(aImage), 101 mLexer(Transition::ToUnbuffered(State::FINISHED_PNG_DATA, State::PNG_DATA, 102 SIZE_MAX), 103 Transition::TerminateSuccess()), 104 mNextTransition(Transition::ContinueUnbuffered(State::PNG_DATA)), 105 mLastChunkLength(0), 106 mPNG(nullptr), 107 mInfo(nullptr), 108 mCMSLine(nullptr), 109 interlacebuf(nullptr), 110 mFormat(SurfaceFormat::UNKNOWN), 111 mChannels(0), 112 mPass(0), 113 mFrameIsHidden(false), 114 mDisablePremultipliedAlpha(false), 115 mGotInfoCallback(false), 116 mUsePipeTransform(false), 117 mErrorIsRecoverable(false), 118 mNumFrames(0) {} 119 120 nsPNGDecoder::~nsPNGDecoder() { 121 if (mPNG) { 122 png_destroy_read_struct(&mPNG, mInfo ? &mInfo : nullptr, nullptr); 123 } 124 if (mCMSLine) { 125 free(mCMSLine); 126 } 127 if (interlacebuf) { 128 free(interlacebuf); 129 } 130 } 131 132 nsPNGDecoder::TransparencyType nsPNGDecoder::GetTransparencyType( 133 const UnorientedIntRect& aFrameRect) { 134 MOZ_ASSERT(GetOrientation().IsIdentity() || !HasAnimation(), 135 "can't be oriented and have animation"); 136 137 // Check if the image has a transparent color in its palette. 138 if (HasAlphaChannel()) { 139 return TransparencyType::eAlpha; 140 } 141 if (!aFrameRect.IsEqualEdges( 142 UnorientedIntRect(IntPointTyped<mozilla::UnorientedPixel>(0, 0), 143 GetOrientation().ToUnoriented(Size())))) { 144 MOZ_ASSERT(HasAnimation()); 145 return TransparencyType::eFrameRect; 146 } 147 148 return TransparencyType::eNone; 149 } 150 151 void nsPNGDecoder::PostHasTransparencyIfNeeded( 152 TransparencyType aTransparencyType) { 153 switch (aTransparencyType) { 154 case TransparencyType::eNone: 155 return; 156 157 case TransparencyType::eAlpha: 158 PostHasTransparency(); 159 return; 160 161 case TransparencyType::eFrameRect: 162 // If the first frame of animated image doesn't draw into the whole image, 163 // then record that it is transparent. For subsequent frames, this doesn't 164 // affect transparency, because they're composited on top of all previous 165 // frames. 166 if (mNumFrames == 0) { 167 PostHasTransparency(); 168 } 169 return; 170 } 171 } 172 173 // CreateFrame() is used for both simple and animated images. 174 nsresult nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo) { 175 MOZ_ASSERT(HasSize()); 176 MOZ_ASSERT(!IsMetadataDecode()); 177 178 // Check if we have transparency, and send notifications if needed. 179 auto transparency = GetTransparencyType(aFrameInfo.mFrameRect); 180 PostHasTransparencyIfNeeded(transparency); 181 mFormat = transparency == TransparencyType::eNone ? SurfaceFormat::OS_RGBX 182 : SurfaceFormat::OS_RGBA; 183 184 // Make sure there's no animation or padding if we're downscaling. 185 MOZ_ASSERT_IF(Size() != OutputSize(), mNumFrames == 0); 186 MOZ_ASSERT_IF(Size() != OutputSize(), !GetImageMetadata().HasAnimation()); 187 MOZ_ASSERT_IF(Size() != OutputSize(), 188 transparency != TransparencyType::eFrameRect); 189 190 Maybe<AnimationParams> animParams; 191 #ifdef PNG_APNG_SUPPORTED 192 const bool isAnimated = png_get_valid(mPNG, mInfo, PNG_INFO_acTL); 193 if (!IsFirstFrameDecode() && isAnimated) { 194 mAnimInfo = AnimFrameInfo(mPNG, mInfo); 195 196 if (mAnimInfo.mDispose == DisposalMethod::CLEAR) { 197 // We may have to display the background under this image during 198 // animation playback, so we regard it as transparent. 199 PostHasTransparency(); 200 } 201 202 animParams.emplace( 203 AnimationParams{aFrameInfo.mFrameRect.ToUnknownRect(), 204 FrameTimeout::FromRawMilliseconds(mAnimInfo.mTimeout), 205 mNumFrames, mAnimInfo.mBlend, mAnimInfo.mDispose}); 206 } 207 #endif 208 209 MOZ_ASSERT(GetOrientation().IsIdentity() || !animParams.isSome(), 210 "can't be oriented and have animation"); 211 MOZ_ASSERT(GetOrientation().IsIdentity() || !aFrameInfo.mIsInterlaced, 212 "can't be oriented and be doing interlacing"); 213 214 const bool wantToReorient = !GetOrientation().IsIdentity(); 215 216 #ifdef DEBUG 217 const bool isFullFrame = aFrameInfo.mFrameRect.IsEqualEdges( 218 UnorientedIntRect(IntPointTyped<mozilla::UnorientedPixel>(0, 0), 219 GetOrientation().ToUnoriented(Size()))); 220 # ifdef PNG_APNG_SUPPORTED 221 MOZ_ASSERT(isAnimated || isFullFrame, 222 "can only have partial frames if animated"); 223 # endif 224 MOZ_ASSERT(!wantToReorient || isFullFrame, 225 "can only have partial frames if not re-orienting"); 226 #endif 227 228 SurfacePipeFlags pipeFlags = SurfacePipeFlags(); 229 230 // We disable progressive display if we are reoriented because we don't 231 // support that yet in the reorienting pipeline. And the Adam7 flag doesn't do 232 // anything unless the progressive display flag is passed, so we've already 233 // disabled interlacing by the time we get here if we are reorienting (but we 234 // check again for symmetry). 235 if (!wantToReorient) { 236 if (mNumFrames == 0) { 237 // The first frame may be displayed progressively. 238 pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY; 239 } 240 241 if (aFrameInfo.mIsInterlaced) { 242 // If this image is interlaced, we can display better quality intermediate 243 // results to the user by post processing them with 244 // ADAM7InterpolatingFilter. 245 pipeFlags |= SurfacePipeFlags::ADAM7_INTERPOLATE; 246 } 247 } 248 249 SurfaceFormat inFormat; 250 if (mTransform && !mUsePipeTransform) { 251 // QCMS will output in the correct format. 252 inFormat = mFormat; 253 } else if (transparency == TransparencyType::eAlpha) { 254 // We are outputting directly as RGBA, so we need to swap at this step. 255 inFormat = SurfaceFormat::R8G8B8A8; 256 } else { 257 // We have no alpha channel, so we need to unpack from RGB to BGRA. 258 inFormat = SurfaceFormat::R8G8B8; 259 } 260 261 // Only apply premultiplication if the frame has true alpha. If we ever 262 // support downscaling animated images, we will need to premultiply for frame 263 // rect transparency when downscaling as well. 264 if (transparency == TransparencyType::eAlpha && !mDisablePremultipliedAlpha) { 265 pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA; 266 } 267 268 qcms_transform* pipeTransform = mUsePipeTransform ? mTransform : nullptr; 269 Maybe<SurfacePipe> pipe; 270 if (!wantToReorient) { 271 // If we get here then the orientation is the identity, so it is valid to 272 // convert mFrameRect directly from Unoriented to Oriented. 273 pipe = SurfacePipeFactory::CreateSurfacePipe( 274 this, Size(), OutputSize(), 275 OrientedIntRect::FromUnknownRect(aFrameInfo.mFrameRect.ToUnknownRect()), 276 inFormat, mFormat, animParams, pipeTransform, pipeFlags); 277 } else { 278 pipe = SurfacePipeFactory::CreateReorientSurfacePipe( 279 this, Size(), OutputSize(), inFormat, mFormat, pipeTransform, 280 GetOrientation(), pipeFlags); 281 } 282 283 if (!pipe) { 284 mPipe = SurfacePipe(); 285 return NS_ERROR_FAILURE; 286 } 287 288 mPipe = std::move(*pipe); 289 290 mFrameRect = aFrameInfo.mFrameRect; 291 mPass = 0; 292 293 MOZ_LOG(sPNGDecoderAccountingLog, LogLevel::Debug, 294 ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created " 295 "image frame with %dx%d pixels for decoder %p", 296 mFrameRect.Width(), mFrameRect.Height(), this)); 297 298 return NS_OK; 299 } 300 301 // set timeout and frame disposal method for the current frame 302 void nsPNGDecoder::EndImageFrame() { 303 if (mFrameIsHidden) { 304 return; 305 } 306 307 mNumFrames++; 308 309 Opacity opacity = mFormat == SurfaceFormat::OS_RGBX 310 ? Opacity::FULLY_OPAQUE 311 : Opacity::SOME_TRANSPARENCY; 312 313 PostFrameStop(opacity); 314 } 315 316 nsresult nsPNGDecoder::InitInternal() { 317 mDisablePremultipliedAlpha = 318 bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA); 319 320 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED 321 static png_byte color_chunks[] = {99, 72, 82, 77, '\0', // cHRM 322 105, 67, 67, 80, '\0'}; // iCCP 323 static png_byte unused_chunks[] = {98, 75, 71, 68, '\0', // bKGD 324 104, 73, 83, 84, '\0', // hIST 325 105, 84, 88, 116, '\0', // iTXt 326 111, 70, 70, 115, '\0', // oFFs 327 112, 67, 65, 76, '\0', // pCAL 328 115, 67, 65, 76, '\0', // sCAL 329 112, 72, 89, 115, '\0', // pHYs 330 115, 66, 73, 84, '\0', // sBIT 331 115, 80, 76, 84, '\0', // sPLT 332 116, 69, 88, 116, '\0', // tEXt 333 116, 73, 77, 69, '\0', // tIME 334 122, 84, 88, 116, '\0'}; // zTXt 335 #endif 336 337 // Initialize the container's source image header 338 // Always decode to 24 bit pixdepth 339 340 mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, 341 nsPNGDecoder::error_callback, 342 nsPNGDecoder::warning_callback); 343 if (!mPNG) { 344 return NS_ERROR_OUT_OF_MEMORY; 345 } 346 347 mInfo = png_create_info_struct(mPNG); 348 if (!mInfo) { 349 png_destroy_read_struct(&mPNG, nullptr, nullptr); 350 return NS_ERROR_OUT_OF_MEMORY; 351 } 352 353 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED 354 // Ignore unused chunks 355 if (mCMSMode == CMSMode::Off || IsMetadataDecode()) { 356 png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2); 357 } 358 359 png_set_keep_unknown_chunks(mPNG, 1, unused_chunks, 360 (int)sizeof(unused_chunks) / 5); 361 #endif 362 363 #ifdef PNG_SET_USER_LIMITS_SUPPORTED 364 png_set_user_limits(mPNG, MOZ_PNG_MAX_WIDTH, MOZ_PNG_MAX_HEIGHT); 365 png_set_chunk_malloc_max(mPNG, kPngMaxChunkSize); 366 #endif 367 368 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED 369 // Disallow palette-index checking, for speed; we would ignore the warning 370 // anyhow. This feature was added at libpng version 1.5.10 and is disabled 371 // in the embedded libpng but enabled by default in the system libpng. This 372 // call also disables it in the system libpng, for decoding speed. 373 // Bug #745202. 374 png_set_check_for_invalid_index(mPNG, 0); 375 #endif 376 377 #ifdef PNG_SET_OPTION_SUPPORTED 378 # if defined(PNG_sRGB_PROFILE_CHECKS) && PNG_sRGB_PROFILE_CHECKS >= 0 379 // Skip checking of sRGB ICC profiles 380 png_set_option(mPNG, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); 381 # endif 382 383 # ifdef PNG_MAXIMUM_INFLATE_WINDOW 384 // Force a larger zlib inflate window as some images in the wild have 385 // incorrectly set metadata (specifically CMF bits) which prevent us from 386 // decoding them otherwise. 387 png_set_option(mPNG, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON); 388 # endif 389 #endif 390 391 // use this as libpng "progressive pointer" (retrieve in callbacks) 392 png_set_progressive_read_fn( 393 mPNG, static_cast<png_voidp>(this), nsPNGDecoder::info_callback, 394 nsPNGDecoder::row_callback, nsPNGDecoder::end_callback); 395 396 return NS_OK; 397 } 398 399 LexerResult nsPNGDecoder::DoDecode(SourceBufferIterator& aIterator, 400 IResumable* aOnResume) { 401 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!"); 402 403 return mLexer.Lex(aIterator, aOnResume, 404 [=](State aState, const char* aData, size_t aLength) { 405 switch (aState) { 406 case State::PNG_DATA: 407 return ReadPNGData(aData, aLength); 408 case State::FINISHED_PNG_DATA: 409 return FinishedPNGData(); 410 } 411 MOZ_CRASH("Unknown State"); 412 }); 413 } 414 415 LexerTransition<nsPNGDecoder::State> nsPNGDecoder::ReadPNGData( 416 const char* aData, size_t aLength) { 417 // If we were waiting until after returning from a yield to call 418 // CreateFrame(), call it now. 419 if (mNextFrameInfo) { 420 if (NS_FAILED(CreateFrame(*mNextFrameInfo))) { 421 return Transition::TerminateFailure(); 422 } 423 424 MOZ_ASSERT(mImageData, "Should have a buffer now"); 425 mNextFrameInfo = Nothing(); 426 } 427 428 // libpng uses setjmp/longjmp for error handling. 429 if (setjmp(png_jmpbuf(mPNG))) { 430 return (GetFrameCount() > 0 && mErrorIsRecoverable) 431 ? Transition::TerminateSuccess() 432 : Transition::TerminateFailure(); 433 } 434 435 // Pass the data off to libpng. 436 mLastChunkLength = aLength; 437 mNextTransition = Transition::ContinueUnbuffered(State::PNG_DATA); 438 png_process_data(mPNG, mInfo, 439 reinterpret_cast<unsigned char*>(const_cast<char*>((aData))), 440 aLength); 441 442 // Make sure that we've reached a terminal state if decoding is done. 443 MOZ_ASSERT_IF(GetDecodeDone(), mNextTransition.NextStateIsTerminal()); 444 MOZ_ASSERT_IF(HasError(), mNextTransition.NextStateIsTerminal()); 445 446 // Continue with whatever transition the callback code requested. We 447 // initialized this to Transition::ContinueUnbuffered(State::PNG_DATA) above, 448 // so by default we just continue the unbuffered read. 449 return mNextTransition; 450 } 451 452 LexerTransition<nsPNGDecoder::State> nsPNGDecoder::FinishedPNGData() { 453 // Since we set up an unbuffered read for SIZE_MAX bytes, if we actually read 454 // all that data something is really wrong. 455 MOZ_ASSERT_UNREACHABLE("Read the entire address space?"); 456 return Transition::TerminateFailure(); 457 } 458 459 // Sets up gamma pre-correction in libpng before our callback gets called. 460 // We need to do this if we don't end up with a CMS profile. 461 static void PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr) { 462 double aGamma; 463 464 if (png_get_gAMA(png_ptr, info_ptr, &aGamma)) { 465 if ((aGamma <= 0.0) || (aGamma > 21474.83)) { 466 aGamma = 0.45455; 467 png_set_gAMA(png_ptr, info_ptr, aGamma); 468 } 469 png_set_gamma(png_ptr, 2.2, aGamma); 470 } else { 471 png_set_gamma(png_ptr, 2.2, 0.45455); 472 } 473 } 474 475 // Adapted from http://www.littlecms.com/pngchrm.c example code 476 uint32_t nsPNGDecoder::ReadColorProfile(png_structp png_ptr, png_infop info_ptr, 477 int color_type, bool* sRGBTag) { 478 // Check if cICP chunk is present 479 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_cICP)) { 480 png_byte primaries; 481 png_byte tc; 482 png_byte matrix_coefficients; 483 png_byte range; 484 if (png_get_cICP(png_ptr, info_ptr, &primaries, &tc, &matrix_coefficients, 485 &range)) { 486 if (matrix_coefficients == 0 && range <= 1) { 487 if (range == 0) { 488 MOZ_LOG(sPNGLog, LogLevel::Warning, 489 ("limited range specified in cicp chunk not properly " 490 "supported\n")); 491 } 492 493 mInProfile = qcms_profile_create_cicp( 494 primaries, ChooseTransferCharacteristics(tc)); 495 if (mInProfile) { 496 if (!(color_type & PNG_COLOR_MASK_COLOR)) { 497 png_set_gray_to_rgb(png_ptr); 498 } 499 return qcms_profile_get_rendering_intent(mInProfile); 500 } 501 } 502 } 503 } 504 505 // Check if iCCP chunk is present 506 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { 507 png_uint_32 profileLen; 508 png_bytep profileData; 509 png_charp profileName; 510 int compression; 511 512 png_get_iCCP(png_ptr, info_ptr, &profileName, &compression, &profileData, 513 &profileLen); 514 515 mInProfile = qcms_profile_from_memory((char*)profileData, profileLen); 516 if (mInProfile) { 517 uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); 518 519 bool mismatch = false; 520 if (color_type & PNG_COLOR_MASK_COLOR) { 521 if (profileSpace != icSigRgbData) { 522 mismatch = true; 523 } 524 } else { 525 if (profileSpace == icSigRgbData) { 526 png_set_gray_to_rgb(png_ptr); 527 } else if (profileSpace != icSigGrayData) { 528 mismatch = true; 529 } 530 } 531 532 if (mismatch) { 533 qcms_profile_release(mInProfile); 534 mInProfile = nullptr; 535 } else { 536 return qcms_profile_get_rendering_intent(mInProfile); 537 } 538 } 539 } 540 541 // Check sRGB chunk 542 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { 543 *sRGBTag = true; 544 545 int fileIntent; 546 png_set_gray_to_rgb(png_ptr); 547 png_get_sRGB(png_ptr, info_ptr, &fileIntent); 548 uint32_t map[] = {QCMS_INTENT_PERCEPTUAL, QCMS_INTENT_RELATIVE_COLORIMETRIC, 549 QCMS_INTENT_SATURATION, 550 QCMS_INTENT_ABSOLUTE_COLORIMETRIC}; 551 return map[fileIntent]; 552 } 553 554 // Check gAMA/cHRM chunks 555 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) && 556 png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) { 557 qcms_CIE_xyYTRIPLE primaries; 558 qcms_CIE_xyY whitePoint; 559 560 png_get_cHRM(png_ptr, info_ptr, &whitePoint.x, &whitePoint.y, 561 &primaries.red.x, &primaries.red.y, &primaries.green.x, 562 &primaries.green.y, &primaries.blue.x, &primaries.blue.y); 563 whitePoint.Y = primaries.red.Y = primaries.green.Y = primaries.blue.Y = 1.0; 564 565 double gammaOfFile; 566 567 png_get_gAMA(png_ptr, info_ptr, &gammaOfFile); 568 569 mInProfile = qcms_profile_create_rgb_with_gamma(whitePoint, primaries, 570 1.0 / gammaOfFile); 571 572 if (mInProfile) { 573 png_set_gray_to_rgb(png_ptr); 574 } 575 } 576 577 return QCMS_INTENT_PERCEPTUAL; // Our default 578 } 579 580 void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) { 581 png_uint_32 width, height; 582 int bit_depth, color_type, interlace_type, compression_type, filter_type; 583 unsigned int channels; 584 585 png_bytep trans = nullptr; 586 int num_trans = 0; 587 588 nsPNGDecoder* decoder = 589 static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); 590 591 if (decoder->mGotInfoCallback) { 592 MOZ_LOG(sPNGLog, LogLevel::Warning, 593 ("libpng called info_callback more than once\n")); 594 return; 595 } 596 597 decoder->mGotInfoCallback = true; 598 599 // Always decode to 24-bit RGB or 32-bit RGBA 600 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 601 &interlace_type, &compression_type, &filter_type); 602 603 #ifdef PNG_APNG_SUPPORTED 604 const bool isAnimated = png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL); 605 #endif 606 607 // We only support exif orientation for non-animated images. 608 png_uint_32 num_exif_bytes = 0; 609 png_bytep exifdata = nullptr; 610 if ( 611 #ifdef PNG_APNG_SUPPORTED 612 !isAnimated && 613 #endif 614 png_get_eXIf_1(png_ptr, info_ptr, &num_exif_bytes, &exifdata) && 615 num_exif_bytes > 0 && exifdata) { 616 617 EXIFData exif = EXIFParser::Parse(/* aExpectExifIdCode = */ false, exifdata, 618 static_cast<uint32_t>(num_exif_bytes), 619 gfx::IntSize(width, height)); 620 decoder->PostSize(width, height, exif.orientation, exif.resolution); 621 } else { 622 decoder->PostSize(width, height); 623 } 624 625 const UnorientedIntRect frameRect(0, 0, width, height); 626 627 if (width > SurfaceCache::MaximumCapacity() / (bit_depth > 8 ? 16 : 8)) { 628 // libpng needs space to allocate two row buffers 629 png_error(decoder->mPNG, "Image is too wide"); 630 } 631 632 if (decoder->HasError()) { 633 // Setting the size led to an error. 634 png_error(decoder->mPNG, "Sizing error"); 635 } 636 637 if (color_type == PNG_COLOR_TYPE_PALETTE) { 638 png_set_expand(png_ptr); 639 } 640 641 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { 642 png_set_expand(png_ptr); 643 } 644 645 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { 646 png_color_16p trans_values; 647 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); 648 if (num_trans != 0) { 649 png_set_expand(png_ptr); 650 } 651 } 652 653 if (bit_depth == 16) { 654 png_set_scale_16(png_ptr); 655 } 656 657 // We only need to extract the color profile for non-metadata decodes. It is 658 // fairly expensive to read the profile and create the transform so we should 659 // avoid it if not necessary. 660 uint32_t intent = -1; 661 bool sRGBTag = false; 662 if (!decoder->IsMetadataDecode()) { 663 if (decoder->mCMSMode != CMSMode::Off) { 664 intent = gfxPlatform::GetRenderingIntent(); 665 uint32_t pIntent = 666 decoder->ReadColorProfile(png_ptr, info_ptr, color_type, &sRGBTag); 667 // If we're not mandating an intent, use the one from the image. 668 if (intent == uint32_t(-1)) { 669 intent = pIntent; 670 } 671 } 672 const bool hasColorInfo = decoder->mInProfile || sRGBTag; 673 if (!hasColorInfo || !decoder->GetCMSOutputProfile()) { 674 png_set_gray_to_rgb(png_ptr); 675 676 // only do gamma correction if CMS isn't entirely disabled 677 if (decoder->mCMSMode != CMSMode::Off) { 678 PNGDoGammaCorrection(png_ptr, info_ptr); 679 } 680 } 681 } 682 683 // Let libpng expand interlaced images. 684 // We only support interlacing for images that aren't rotated with exif data. 685 const bool isInterlaced = (interlace_type == PNG_INTERLACE_ADAM7) && 686 decoder->GetOrientation().IsIdentity(); 687 if (isInterlaced) { 688 png_set_interlace_handling(png_ptr); 689 } 690 691 // now all of those things we set above are used to update various struct 692 // members and whatnot, after which we can get channels, rowbytes, etc. 693 png_read_update_info(png_ptr, info_ptr); 694 decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr); 695 696 //---------------------------------------------------------------// 697 // copy PNG info into imagelib structs (formerly png_set_dims()) // 698 //---------------------------------------------------------------// 699 700 if (channels < 1 || channels > 4) { 701 png_error(decoder->mPNG, "Invalid number of channels"); 702 } 703 704 #ifdef PNG_APNG_SUPPORTED 705 if (isAnimated) { 706 int32_t rawTimeout = GetNextFrameDelay(png_ptr, info_ptr); 707 decoder->PostIsAnimated(FrameTimeout::FromRawMilliseconds(rawTimeout)); 708 709 if (decoder->Size() != decoder->OutputSize() && 710 !decoder->IsFirstFrameDecode()) { 711 MOZ_ASSERT_UNREACHABLE( 712 "Doing downscale-during-decode " 713 "for an animated image?"); 714 png_error(decoder->mPNG, "Invalid downscale attempt"); // Abort decode. 715 } 716 } 717 #endif 718 719 auto transparency = decoder->GetTransparencyType(frameRect); 720 if (decoder->IsMetadataDecode()) { 721 // If we are animated then the first frame rect is either: 722 // 1) the whole image if the IDAT chunk is part of the animation 723 // 2) the frame rect of the first fDAT chunk otherwise. 724 // If we are not animated then we want to make sure to call 725 // PostHasTransparency in the metadata decode if we need to. So it's 726 // okay to pass IntRect(0, 0, width, height) here for animated images; 727 // they will call with the proper first frame rect in the full decode. 728 decoder->PostHasTransparencyIfNeeded(transparency); 729 730 // We have the metadata we're looking for, so stop here, before we allocate 731 // buffers below. 732 return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS); 733 } 734 735 if (decoder->mInProfile && decoder->GetCMSOutputProfile()) { 736 qcms_data_type inType; 737 qcms_data_type outType; 738 739 uint32_t profileSpace = qcms_profile_get_color_space(decoder->mInProfile); 740 decoder->mUsePipeTransform = profileSpace != icSigGrayData; 741 if (decoder->mUsePipeTransform) { 742 // libpng outputs data in RGBA order and we want our final output to be 743 // BGRA order. SurfacePipe takes care of this for us but unfortunately the 744 // swizzle to change the order can happen before or after color management 745 // depending on if we have alpha. If we have alpha then the order will be 746 // color management then swizzle. If we do not have alpha then the order 747 // will be swizzle then color management. See CreateSurfacePipe 748 // https://searchfox.org/mozilla-central/rev/7d6651d29c5c1620bc059f879a3e9bbfb53f271f/image/SurfacePipeFactory.h#133-145 749 if (transparency == TransparencyType::eAlpha) { 750 inType = QCMS_DATA_RGBA_8; 751 outType = QCMS_DATA_RGBA_8; 752 } else { 753 inType = gfxPlatform::GetCMSOSRGBAType(); 754 outType = inType; 755 } 756 } else { 757 // qcms operates on the data before we hand it to SurfacePipe. 758 if (color_type & PNG_COLOR_MASK_ALPHA) { 759 inType = QCMS_DATA_GRAYA_8; 760 outType = gfxPlatform::GetCMSOSRGBAType(); 761 } else { 762 inType = QCMS_DATA_GRAY_8; 763 outType = gfxPlatform::GetCMSOSRGBAType(); 764 } 765 } 766 767 decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, 768 decoder->GetCMSOutputProfile(), 769 outType, (qcms_intent)intent); 770 } else if ((sRGBTag && decoder->mCMSMode == CMSMode::TaggedOnly) || 771 decoder->mCMSMode == CMSMode::All) { 772 // See comment above about SurfacePipe, color management and ordering. 773 decoder->mUsePipeTransform = true; 774 if (transparency == TransparencyType::eAlpha) { 775 decoder->mTransform = 776 decoder->GetCMSsRGBTransform(SurfaceFormat::R8G8B8A8); 777 } else { 778 decoder->mTransform = 779 decoder->GetCMSsRGBTransform(SurfaceFormat::OS_RGBA); 780 } 781 } 782 783 #ifdef PNG_APNG_SUPPORTED 784 if (isAnimated) { 785 png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, 786 nullptr); 787 } 788 789 if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { 790 decoder->mFrameIsHidden = true; 791 } else { 792 #endif 793 nsresult rv = decoder->CreateFrame(FrameInfo{frameRect, isInterlaced}); 794 if (NS_FAILED(rv)) { 795 png_error(decoder->mPNG, "CreateFrame failed"); 796 } 797 MOZ_ASSERT(decoder->mImageData, "Should have a buffer now"); 798 #ifdef PNG_APNG_SUPPORTED 799 } 800 #endif 801 802 if (decoder->mTransform && !decoder->mUsePipeTransform) { 803 decoder->mCMSLine = 804 static_cast<uint8_t*>(malloc(sizeof(uint32_t) * frameRect.Width())); 805 if (!decoder->mCMSLine) { 806 png_error(decoder->mPNG, "malloc of mCMSLine failed"); 807 } 808 } 809 810 if (isInterlaced) { 811 if (frameRect.Height() < 812 INT32_MAX / (frameRect.Width() * int32_t(channels))) { 813 const size_t bufferSize = 814 channels * frameRect.Width() * frameRect.Height(); 815 816 if (bufferSize > SurfaceCache::MaximumCapacity()) { 817 png_error(decoder->mPNG, "Insufficient memory to deinterlace image"); 818 } 819 820 decoder->interlacebuf = static_cast<uint8_t*>(malloc(bufferSize)); 821 } 822 if (!decoder->interlacebuf) { 823 png_error(decoder->mPNG, "malloc of interlacebuf failed"); 824 } 825 } 826 } 827 828 void nsPNGDecoder::PostInvalidationIfNeeded() { 829 Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect(); 830 if (!invalidRect) { 831 return; 832 } 833 834 PostInvalidation(invalidRect->mInputSpaceRect, 835 Some(invalidRect->mOutputSpaceRect)); 836 } 837 838 void nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row, 839 png_uint_32 row_num, int pass) { 840 /* libpng comments: 841 * 842 * This function is called for every row in the image. If the 843 * image is interlacing, and you turned on the interlace handler, 844 * this function will be called for every row in every pass. 845 * Some of these rows will not be changed from the previous pass. 846 * When the row is not changed, the new_row variable will be 847 * nullptr. The rows and passes are called in order, so you don't 848 * really need the row_num and pass, but I'm supplying them 849 * because it may make your life easier. 850 * 851 * For the non-nullptr rows of interlaced images, you must call 852 * png_progressive_combine_row() passing in the row and the 853 * old row. You can call this function for nullptr rows (it will 854 * just return) and for non-interlaced images (it just does the 855 * memcpy for you) if it will make the code easier. Thus, you 856 * can just do this for all cases: 857 * 858 * png_progressive_combine_row(png_ptr, old_row, new_row); 859 * 860 * where old_row is what was displayed for previous rows. Note 861 * that the first pass (pass == 0 really) will completely cover 862 * the old row, so the rows do not have to be initialized. After 863 * the first pass (and only for interlaced images), you will have 864 * to pass the current row, and the function will combine the 865 * old row and the new row. 866 */ 867 nsPNGDecoder* decoder = 868 static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); 869 870 if (decoder->mFrameIsHidden) { 871 return; // Skip this frame. 872 } 873 874 MOZ_ASSERT_IF(decoder->IsFirstFrameDecode(), decoder->mNumFrames == 0); 875 876 while (pass > decoder->mPass) { 877 // Advance to the next pass. We may have to do this multiple times because 878 // libpng will skip passes if the image is so small that no pixels have 879 // changed on a given pass, but ADAM7InterpolatingFilter needs to be reset 880 // once for every pass to perform interpolation properly. 881 decoder->mPipe.ResetToFirstRow(); 882 decoder->mPass++; 883 } 884 885 const png_uint_32 height = 886 static_cast<png_uint_32>(decoder->mFrameRect.Height()); 887 888 if (row_num >= height) { 889 // Bail if we receive extra rows. This is especially important because if we 890 // didn't, we might overflow the deinterlacing buffer. 891 MOZ_ASSERT_UNREACHABLE("libpng producing extra rows?"); 892 return; 893 } 894 895 // Note that |new_row| may be null here, indicating that this is an interlaced 896 // image and |row_callback| is being called for a row that hasn't changed. 897 MOZ_ASSERT_IF(!new_row, decoder->interlacebuf); 898 899 if (decoder->interlacebuf) { 900 uint32_t width = uint32_t(decoder->mFrameRect.Width()); 901 902 // We'll output the deinterlaced version of the row. 903 uint8_t* rowToWrite = 904 decoder->interlacebuf + (row_num * decoder->mChannels * width); 905 906 // Update the deinterlaced version of this row with the new data. 907 png_progressive_combine_row(png_ptr, rowToWrite, new_row); 908 909 decoder->WriteRow(rowToWrite); 910 } else { 911 decoder->WriteRow(new_row); 912 } 913 } 914 915 void nsPNGDecoder::WriteRow(uint8_t* aRow) { 916 MOZ_ASSERT(aRow); 917 918 uint8_t* rowToWrite = aRow; 919 uint32_t width = uint32_t(mFrameRect.Width()); 920 921 // Apply color management to the row, if necessary, before writing it out. 922 // This is only needed for grayscale images. 923 if (mTransform && !mUsePipeTransform) { 924 MOZ_ASSERT(mCMSLine); 925 qcms_transform_data(mTransform, rowToWrite, mCMSLine, width); 926 rowToWrite = mCMSLine; 927 } 928 929 // Write this row to the SurfacePipe. 930 DebugOnly<WriteState> result = 931 mPipe.WriteBuffer(reinterpret_cast<uint32_t*>(rowToWrite)); 932 MOZ_ASSERT(WriteState(result) != WriteState::FAILURE); 933 934 PostInvalidationIfNeeded(); 935 } 936 937 void nsPNGDecoder::DoTerminate(png_structp aPNGStruct, TerminalState aState) { 938 // Stop processing data. Note that we intentionally ignore the return value of 939 // png_process_data_pause(), which tells us how many bytes of the data that 940 // was passed to png_process_data() have not been consumed yet, because now 941 // that we've reached a terminal state, we won't do any more decoding or call 942 // back into libpng anymore. 943 png_process_data_pause(aPNGStruct, /* save = */ false); 944 945 mNextTransition = aState == TerminalState::SUCCESS 946 ? Transition::TerminateSuccess() 947 : Transition::TerminateFailure(); 948 } 949 950 void nsPNGDecoder::DoYield(png_structp aPNGStruct) { 951 // Pause data processing. png_process_data_pause() returns how many bytes of 952 // the data that was passed to png_process_data() have not been consumed yet. 953 // We use this information to tell StreamingLexer where to place us in the 954 // input stream when we come back from the yield. 955 png_size_t pendingBytes = png_process_data_pause(aPNGStruct, 956 /* save = */ false); 957 958 MOZ_ASSERT(pendingBytes < mLastChunkLength); 959 size_t consumedBytes = mLastChunkLength - min(pendingBytes, mLastChunkLength); 960 961 mNextTransition = 962 Transition::ContinueUnbufferedAfterYield(State::PNG_DATA, consumedBytes); 963 } 964 965 nsresult nsPNGDecoder::FinishInternal() { 966 // We shouldn't be called in error cases. 967 MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!"); 968 969 int32_t loop_count = 0; 970 uint32_t frame_count = 1; 971 #ifdef PNG_APNG_SUPPORTED 972 uint32_t num_plays = 0; 973 if (png_get_acTL(mPNG, mInfo, &frame_count, &num_plays)) { 974 loop_count = int32_t(num_plays) - 1; 975 } else { 976 frame_count = 1; 977 } 978 #endif 979 980 PostLoopCount(loop_count); 981 982 if (WantsFrameCount()) { 983 PostFrameCount(frame_count); 984 } 985 986 if (IsMetadataDecode()) { 987 return NS_OK; 988 } 989 990 if (InFrame()) { 991 EndImageFrame(); 992 } 993 PostDecodeDone(); 994 995 return NS_OK; 996 } 997 998 #ifdef PNG_APNG_SUPPORTED 999 // got the header of a new frame that's coming 1000 void nsPNGDecoder::frame_info_callback(png_structp png_ptr, 1001 png_uint_32 frame_num) { 1002 nsPNGDecoder* decoder = 1003 static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); 1004 1005 // old frame is done 1006 decoder->EndImageFrame(); 1007 1008 const bool previousFrameWasHidden = decoder->mFrameIsHidden; 1009 1010 if (!previousFrameWasHidden && decoder->IsFirstFrameDecode()) { 1011 // We're about to get a second non-hidden frame, but we only want the first. 1012 // Stop decoding now. (And avoid allocating the unnecessary buffers below.) 1013 return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS); 1014 } 1015 1016 // Only the first frame can be hidden, so unhide unconditionally here. 1017 decoder->mFrameIsHidden = false; 1018 1019 // Save the information necessary to create the frame; we'll actually create 1020 // it when we return from the yield. 1021 const UnorientedIntRect frameRect( 1022 png_get_next_frame_x_offset(png_ptr, decoder->mInfo), 1023 png_get_next_frame_y_offset(png_ptr, decoder->mInfo), 1024 png_get_next_frame_width(png_ptr, decoder->mInfo), 1025 png_get_next_frame_height(png_ptr, decoder->mInfo)); 1026 const bool isInterlaced = bool(decoder->interlacebuf); 1027 1028 # ifndef MOZ_EMBEDDED_LIBPNG 1029 // if using system library, check frame_width and height against 0 1030 if (frameRect.width == 0) { 1031 png_error(png_ptr, "Frame width must not be 0"); 1032 } 1033 if (frameRect.height == 0) { 1034 png_error(png_ptr, "Frame height must not be 0"); 1035 } 1036 # endif 1037 1038 const FrameInfo info{frameRect, isInterlaced}; 1039 1040 // If the previous frame was hidden, skip the yield (which will mislead the 1041 // caller, who will think the previous frame was real) and just allocate the 1042 // new frame here. 1043 if (previousFrameWasHidden) { 1044 if (NS_FAILED(decoder->CreateFrame(info))) { 1045 return decoder->DoTerminate(png_ptr, TerminalState::FAILURE); 1046 } 1047 1048 MOZ_ASSERT(decoder->mImageData, "Should have a buffer now"); 1049 return; // No yield, so we'll just keep decoding. 1050 } 1051 1052 // Yield to the caller to notify them that the previous frame is now complete. 1053 decoder->mNextFrameInfo = Some(info); 1054 return decoder->DoYield(png_ptr); 1055 } 1056 #endif 1057 1058 void nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr) { 1059 /* libpng comments: 1060 * 1061 * this function is called when the whole image has been read, 1062 * including any chunks after the image (up to and including 1063 * the IEND). You will usually have the same info chunk as you 1064 * had in the header, although some data may have been added 1065 * to the comments and time fields. 1066 * 1067 * Most people won't do much here, perhaps setting a flag that 1068 * marks the image as finished. 1069 */ 1070 1071 nsPNGDecoder* decoder = 1072 static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); 1073 1074 // We shouldn't get here if we've hit an error 1075 MOZ_ASSERT(!decoder->HasError(), "Finishing up PNG but hit error!"); 1076 1077 return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS); 1078 } 1079 1080 void nsPNGDecoder::error_callback(png_structp png_ptr, 1081 png_const_charp error_msg) { 1082 MOZ_LOG(sPNGLog, LogLevel::Error, ("libpng error: %s\n", error_msg)); 1083 1084 nsPNGDecoder* decoder = 1085 static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); 1086 1087 if (strstr(error_msg, "invalid chunk type") || 1088 strstr(error_msg, "bad header (invalid type)")) { 1089 decoder->mErrorIsRecoverable = true; 1090 } else { 1091 decoder->mErrorIsRecoverable = false; 1092 } 1093 1094 png_longjmp(png_ptr, 1); 1095 } 1096 1097 void nsPNGDecoder::warning_callback(png_structp png_ptr, 1098 png_const_charp warning_msg) { 1099 MOZ_LOG(sPNGLog, LogLevel::Warning, ("libpng warning: %s\n", warning_msg)); 1100 } 1101 1102 Maybe<glean::impl::MemoryDistributionMetric> nsPNGDecoder::SpeedMetric() const { 1103 return Some(glean::image_decode::speed_png); 1104 } 1105 1106 bool nsPNGDecoder::IsValidICOResource() const { 1107 // Only 32-bit RGBA PNGs are valid ICO resources; see here: 1108 // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx 1109 1110 // If there are errors in the call to png_get_IHDR, the error_callback in 1111 // nsPNGDecoder.cpp is called. In this error callback we do a longjmp, so 1112 // we need to save the jump buffer here. Otherwise we'll end up without a 1113 // proper callstack. 1114 if (setjmp(png_jmpbuf(mPNG))) { 1115 // We got here from a longjmp call indirectly from png_get_IHDR via 1116 // error_callback. Ignore mErrorIsRecoverable: if we got an invalid chunk 1117 // error before even reading the IHDR we can't recover from that. 1118 return false; 1119 } 1120 1121 png_uint_32 png_width, // Unused 1122 png_height; // Unused 1123 1124 int png_bit_depth, png_color_type; 1125 1126 if (png_get_IHDR(mPNG, mInfo, &png_width, &png_height, &png_bit_depth, 1127 &png_color_type, nullptr, nullptr, nullptr)) { 1128 return ((png_color_type == PNG_COLOR_TYPE_RGB_ALPHA || 1129 png_color_type == PNG_COLOR_TYPE_RGB) && 1130 png_bit_depth == 8); 1131 } else { 1132 return false; 1133 } 1134 } 1135 1136 } // namespace image 1137 } // namespace mozilla