1/* 2 * Copyright (C) 2006 Apple Inc. 3 * Copyright (C) 2007-2009 Torch Mobile, Inc. 4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 5 * 6 * Portions are Copyright (C) 2001 mozilla.org 7 * 8 * Other contributors: 9 * Stuart Parmenter <stuart@mozilla.com> 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 * Alternatively, the contents of this file may be used under the terms 26 * of either the Mozilla Public License Version 1.1, found at 27 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public 28 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html 29 * (the "GPL"), in which case the provisions of the MPL or the GPL are 30 * applicable instead of those above. If you wish to allow use of your 31 * version of this file only under the terms of one of those two 32 * licenses (the MPL or the GPL) and not to allow others to use your 33 * version of this file under the LGPL, indicate your decision by 34 * deletingthe provisions above and replace them with the notice and 35 * other provisions required by the MPL or the GPL, as the case may be. 36 * If you do not delete the provisions above, a recipient may use your 37 * version of this file under any of the LGPL, the MPL or the GPL. 38 */ 39 40#include "config.h" 41#include "PNGImageDecoder.h" 42 43#include "Color.h" 44#include "png.h" 45#include <wtf/PassOwnPtr.h> 46#include <wtf/StdLibExtras.h> 47 48#if USE(QCMSLIB) 49#include "qcms.h" 50#endif 51 52#if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4)) 53#define JMPBUF(png_ptr) png_jmpbuf(png_ptr) 54#else 55#define JMPBUF(png_ptr) png_ptr->jmpbuf 56#endif 57 58namespace WebCore { 59 60// Gamma constants. 61const double cMaxGamma = 21474.83; 62const double cDefaultGamma = 2.2; 63const double cInverseGamma = 0.45455; 64 65// Protect against large PNGs. See Mozilla's bug #251381 for more info. 66const unsigned long cMaxPNGSize = 1000000UL; 67 68// Called if the decoding of the image fails. 69static void PNGAPI decodingFailed(png_structp png, png_const_charp) 70{ 71 longjmp(JMPBUF(png), 1); 72} 73 74// Callbacks given to the read struct. The first is for warnings (we want to 75// treat a particular warning as an error, which is why we have to register this 76// callback). 77static void PNGAPI decodingWarning(png_structp png, png_const_charp warningMsg) 78{ 79 // Mozilla did this, so we will too. 80 // Convert a tRNS warning to be an error (see 81 // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 ) 82 if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24)) 83 png_error(png, warningMsg); 84} 85 86// Called when we have obtained the header information (including the size). 87static void PNGAPI headerAvailable(png_structp png, png_infop) 88{ 89 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); 90} 91 92// Called when a row is ready. 93static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass) 94{ 95 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); 96} 97 98// Called when we have completely finished decoding the image. 99static void PNGAPI pngComplete(png_structp png, png_infop) 100{ 101 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); 102} 103 104class PNGImageReader { 105 WTF_MAKE_FAST_ALLOCATED; 106public: 107 PNGImageReader(PNGImageDecoder* decoder) 108 : m_readOffset(0) 109 , m_currentBufferSize(0) 110 , m_decodingSizeOnly(false) 111 , m_hasAlpha(false) 112 , m_interlaceBuffer(0) 113#if USE(QCMSLIB) 114 , m_transform(0) 115 , m_rowBuffer() 116#endif 117 { 118 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning); 119 m_info = png_create_info_struct(m_png); 120 png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete); 121 } 122 123 ~PNGImageReader() 124 { 125 close(); 126 } 127 128 void close() 129 { 130 if (m_png && m_info) 131 // This will zero the pointers. 132 png_destroy_read_struct(&m_png, &m_info, 0); 133#if USE(QCMSLIB) 134 if (m_transform) 135 qcms_transform_release(m_transform); 136 m_transform = 0; 137#endif 138 delete[] m_interlaceBuffer; 139 m_interlaceBuffer = 0; 140 m_readOffset = 0; 141 } 142 143 bool decode(const SharedBuffer& data, bool sizeOnly) 144 { 145 m_decodingSizeOnly = sizeOnly; 146 PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png)); 147 148 // We need to do the setjmp here. Otherwise bad things will happen. 149 if (setjmp(JMPBUF(m_png))) 150 return decoder->setFailed(); 151 152 const char* segment; 153 while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) { 154 m_readOffset += segmentLength; 155 m_currentBufferSize = m_readOffset; 156 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength); 157 // We explicitly specify the superclass isSizeAvailable() because we 158 // merely want to check if we've managed to set the size, not 159 // (recursively) trigger additional decoding if we haven't. 160 if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isComplete()) 161 return true; 162 } 163 return false; 164 } 165 166 png_structp pngPtr() const { return m_png; } 167 png_infop infoPtr() const { return m_info; } 168 169 void setReadOffset(unsigned offset) { m_readOffset = offset; } 170 unsigned currentBufferSize() const { return m_currentBufferSize; } 171 bool decodingSizeOnly() const { return m_decodingSizeOnly; } 172 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } 173 bool hasAlpha() const { return m_hasAlpha; } 174 175 png_bytep interlaceBuffer() const { return m_interlaceBuffer; } 176 void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; } 177#if USE(QCMSLIB) 178 png_bytep rowBuffer() const { return m_rowBuffer.get(); } 179 void createRowBuffer(int size) { m_rowBuffer = std::make_unique<png_byte[]>(size); } 180 qcms_transform* colorTransform() const { return m_transform; } 181 182 void createColorTransform(const ColorProfile& colorProfile, bool hasAlpha) 183 { 184 if (m_transform) 185 qcms_transform_release(m_transform); 186 m_transform = 0; 187 188 if (colorProfile.isEmpty()) 189 return; 190 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); 191 if (!deviceProfile) 192 return; 193 qcms_profile* inputProfile = qcms_profile_from_memory(colorProfile.data(), colorProfile.size()); 194 if (!inputProfile) 195 return; 196 // We currently only support color profiles for RGB and RGBA images. 197 ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); 198 qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_8; 199 // FIXME: Don't force perceptual intent if the image profile contains an intent. 200 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProfile, dataFormat, QCMS_INTENT_PERCEPTUAL); 201 qcms_profile_release(inputProfile); 202 } 203#endif 204 205private: 206 png_structp m_png; 207 png_infop m_info; 208 unsigned m_readOffset; 209 unsigned m_currentBufferSize; 210 bool m_decodingSizeOnly; 211 bool m_hasAlpha; 212 png_bytep m_interlaceBuffer; 213#if USE(QCMSLIB) 214 qcms_transform* m_transform; 215 std::unique_ptr<png_byte[]> m_rowBuffer; 216#endif 217}; 218 219PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, 220 ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) 221 : ImageDecoder(alphaOption, gammaAndColorProfileOption) 222 , m_doNothingOnFailure(false) 223{ 224} 225 226PNGImageDecoder::~PNGImageDecoder() 227{ 228} 229 230bool PNGImageDecoder::isSizeAvailable() 231{ 232 if (!ImageDecoder::isSizeAvailable()) 233 decode(true); 234 235 return ImageDecoder::isSizeAvailable(); 236} 237 238bool PNGImageDecoder::setSize(unsigned width, unsigned height) 239{ 240 if (!ImageDecoder::setSize(width, height)) 241 return false; 242 243 prepareScaleDataIfNecessary(); 244 return true; 245} 246 247ImageFrame* PNGImageDecoder::frameBufferAtIndex(size_t index) 248{ 249 if (index) 250 return 0; 251 252 if (m_frameBufferCache.isEmpty()) { 253 m_frameBufferCache.resize(1); 254 m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); 255 } 256 257 ImageFrame& frame = m_frameBufferCache[0]; 258 if (frame.status() != ImageFrame::FrameComplete) 259 decode(false); 260 return &frame; 261} 262 263bool PNGImageDecoder::setFailed() 264{ 265 if (m_doNothingOnFailure) 266 return false; 267 m_reader.clear(); 268 return ImageDecoder::setFailed(); 269} 270 271static void readColorProfile(png_structp png, png_infop info, ColorProfile& colorProfile) 272{ 273 ASSERT(colorProfile.isEmpty()); 274 275#ifdef PNG_iCCP_SUPPORTED 276 char* profileName; 277 int compressionType; 278#if (PNG_LIBPNG_VER < 10500) 279 png_charp profile; 280#else 281 png_bytep profile; 282#endif 283 png_uint_32 profileLength; 284 if (!png_get_iCCP(png, info, &profileName, &compressionType, &profile, &profileLength)) 285 return; 286 287 // Only accept RGB color profiles from input class devices. 288 bool ignoreProfile = false; 289 char* profileData = reinterpret_cast<char*>(profile); 290 if (profileLength < ImageDecoder::iccColorProfileHeaderLength) 291 ignoreProfile = true; 292 else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) 293 ignoreProfile = true; 294 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) 295 ignoreProfile = true; 296 297 if (!ignoreProfile) 298 colorProfile.append(profileData, profileLength); 299#endif 300} 301 302void PNGImageDecoder::headerAvailable() 303{ 304 png_structp png = m_reader->pngPtr(); 305 png_infop info = m_reader->infoPtr(); 306 png_uint_32 width = png_get_image_width(png, info); 307 png_uint_32 height = png_get_image_height(png, info); 308 309 // Protect against large images. 310 if (width > cMaxPNGSize || height > cMaxPNGSize) { 311 longjmp(JMPBUF(png), 1); 312 return; 313 } 314 315 // We can fill in the size now that the header is available. Avoid memory 316 // corruption issues by neutering setFailed() during this call; if we don't 317 // do this, failures will cause |m_reader| to be deleted, and our jmpbuf 318 // will cease to exist. Note that we'll still properly set the failure flag 319 // in this case as soon as we longjmp(). 320 m_doNothingOnFailure = true; 321 bool result = setSize(width, height); 322 m_doNothingOnFailure = false; 323 if (!result) { 324 longjmp(JMPBUF(png), 1); 325 return; 326 } 327 328 int bitDepth, colorType, interlaceType, compressionType, filterType, channels; 329 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); 330 331 // The options we set here match what Mozilla does. 332 333 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 334 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 335 png_set_expand(png); 336 337 png_bytep trns = 0; 338 int trnsCount = 0; 339 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 340 png_get_tRNS(png, info, &trns, &trnsCount, 0); 341 png_set_expand(png); 342 } 343 344 if (bitDepth == 16) 345 png_set_strip_16(png); 346 347 if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 348 png_set_gray_to_rgb(png); 349 350 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { 351 // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting 352 // color profiles for gray-scale images is slightly tricky, at least using the 353 // CoreGraphics ICC library, because we expand gray-scale images to RGB but we 354 // do not similarly transform the color profile. We'd either need to transform 355 // the color profile or we'd need to decode into a gray-scale image buffer and 356 // hand that to CoreGraphics. 357 readColorProfile(png, info, m_colorProfile); 358#if USE(QCMSLIB) 359 bool decodedImageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; 360 m_reader->createColorTransform(m_colorProfile, decodedImageHasAlpha); 361 m_colorProfile.clear(); 362#endif 363 } 364 365 // Deal with gamma and keep it under our control. 366 double gamma; 367 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { 368 if ((gamma <= 0.0) || (gamma > cMaxGamma)) { 369 gamma = cInverseGamma; 370 png_set_gAMA(png, info, gamma); 371 } 372 png_set_gamma(png, cDefaultGamma, gamma); 373 } else 374 png_set_gamma(png, cDefaultGamma, cInverseGamma); 375 376 // Tell libpng to send us rows for interlaced pngs. 377 if (interlaceType == PNG_INTERLACE_ADAM7) 378 png_set_interlace_handling(png); 379 380 // Update our info now. 381 png_read_update_info(png, info); 382 channels = png_get_channels(png, info); 383 ASSERT(channels == 3 || channels == 4); 384 385 m_reader->setHasAlpha(channels == 4); 386 387 if (m_reader->decodingSizeOnly()) { 388 // If we only needed the size, halt the reader. 389#if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)) 390 // '0' argument to png_process_data_pause means: Do not cache unprocessed data. 391 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0)); 392#else 393 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); 394 png->buffer_size = 0; 395#endif 396 } 397} 398 399static inline void setPixelRGB(ImageFrame::PixelData* dest, png_bytep pixel) 400{ 401 *dest = 0xFF000000U | pixel[0] << 16 | pixel[1] << 8 | pixel[2]; 402} 403 404static inline void setPixelRGBA(ImageFrame::PixelData* dest, png_bytep pixel, unsigned char& nonTrivialAlphaMask) 405{ 406 unsigned char a = pixel[3]; 407 *dest = a << 24 | pixel[0] << 16 | pixel[1] << 8 | pixel[2]; 408 nonTrivialAlphaMask |= (255 - a); 409} 410 411static inline void setPixelPremultipliedRGBA(ImageFrame::PixelData* dest, png_bytep pixel, unsigned char& nonTrivialAlphaMask) 412{ 413 unsigned char a = pixel[3]; 414 unsigned char r = fastDivideBy255(pixel[0] * a); 415 unsigned char g = fastDivideBy255(pixel[1] * a); 416 unsigned char b = fastDivideBy255(pixel[2] * a); 417 418 *dest = a << 24 | r << 16 | g << 8 | b; 419 nonTrivialAlphaMask |= (255 - a); 420} 421 422void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) 423{ 424 if (m_frameBufferCache.isEmpty()) 425 return; 426 427 // Initialize the framebuffer if needed. 428 ImageFrame& buffer = m_frameBufferCache[0]; 429 if (buffer.status() == ImageFrame::FrameEmpty) { 430 png_structp png = m_reader->pngPtr(); 431 if (!buffer.setSize(scaledSize().width(), scaledSize().height())) { 432 longjmp(JMPBUF(png), 1); 433 return; 434 } 435 436 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 437 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { 438 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); 439 if (!m_reader->interlaceBuffer()) { 440 longjmp(JMPBUF(png), 1); 441 return; 442 } 443 } 444 445#if USE(QCMSLIB) 446 if (m_reader->colorTransform()) { 447 m_reader->createRowBuffer(colorChannels * size().width()); 448 if (!m_reader->rowBuffer()) { 449 longjmp(JMPBUF(png), 1); 450 return; 451 } 452 } 453#endif 454 buffer.setStatus(ImageFrame::FramePartial); 455 buffer.setHasAlpha(false); 456 buffer.setColorProfile(m_colorProfile); 457 458 // For PNGs, the frame always fills the entire image. 459 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); 460 } 461 462 /* libpng comments (here to explain what follows). 463 * 464 * this function is called for every row in the image. If the 465 * image is interlacing, and you turned on the interlace handler, 466 * this function will be called for every row in every pass. 467 * Some of these rows will not be changed from the previous pass. 468 * When the row is not changed, the new_row variable will be NULL. 469 * The rows and passes are called in order, so you don't really 470 * need the row_num and pass, but I'm supplying them because it 471 * may make your life easier. 472 */ 473 474 // Nothing to do if the row is unchanged, or the row is outside 475 // the image bounds: libpng may send extra rows, ignore them to 476 // make our lives easier. 477 if (!rowBuffer) 478 return; 479 int y = !m_scaled ? rowIndex : scaledY(rowIndex); 480 if (y < 0 || y >= scaledSize().height()) 481 return; 482 483 /* libpng comments (continued). 484 * 485 * For the non-NULL rows of interlaced images, you must call 486 * png_progressive_combine_row() passing in the row and the 487 * old row. You can call this function for NULL rows (it will 488 * just return) and for non-interlaced images (it just does the 489 * memcpy for you) if it will make the code easier. Thus, you 490 * can just do this for all cases: 491 * 492 * png_progressive_combine_row(png_ptr, old_row, new_row); 493 * 494 * where old_row is what was displayed for previous rows. Note 495 * that the first pass (pass == 0 really) will completely cover 496 * the old row, so the rows do not have to be initialized. After 497 * the first pass (and only for interlaced images), you will have 498 * to pass the current row, and the function will combine the 499 * old row and the new row. 500 */ 501 502 bool hasAlpha = m_reader->hasAlpha(); 503 unsigned colorChannels = hasAlpha ? 4 : 3; 504 png_bytep row = rowBuffer; 505 506 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 507 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 508 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 509 } 510 511#if USE(QCMSLIB) 512 if (qcms_transform* transform = m_reader->colorTransform()) { 513 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); 514 row = m_reader->rowBuffer(); 515 } 516#endif 517 518 // Write the decoded row pixels to the frame buffer. 519 ImageFrame::PixelData* address = buffer.getAddr(0, y); 520 int width = scaledSize().width(); 521 unsigned char nonTrivialAlphaMask = 0; 522 523#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) 524 if (m_scaled) { 525 for (int x = 0; x < width; ++x) { 526 png_bytep pixel = row + m_scaledColumns[x] * colorChannels; 527 unsigned alpha = hasAlpha ? pixel[3] : 255; 528 buffer.setRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); 529 nonTrivialAlphaMask |= (255 - alpha); 530 } 531 } else 532#endif 533 { 534 png_bytep pixel = row; 535 if (hasAlpha) { 536 if (buffer.premultiplyAlpha()) { 537 for (int x = 0; x < width; ++x, pixel += 4) 538 setPixelPremultipliedRGBA(address++, pixel, nonTrivialAlphaMask); 539 } else { 540 for (int x = 0; x < width; ++x, pixel += 4) 541 setPixelRGBA(address++, pixel, nonTrivialAlphaMask); 542 } 543 } else { 544 for (int x = 0; x < width; ++x, pixel += 3) 545 setPixelRGB(address++, pixel); 546 } 547 } 548 549 550 if (nonTrivialAlphaMask && !buffer.hasAlpha()) 551 buffer.setHasAlpha(true); 552} 553 554void PNGImageDecoder::pngComplete() 555{ 556 if (!m_frameBufferCache.isEmpty()) 557 m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete); 558} 559 560void PNGImageDecoder::decode(bool onlySize) 561{ 562 if (failed()) 563 return; 564 565 if (!m_reader) 566 m_reader = adoptPtr(new PNGImageReader(this)); 567 568 // If we couldn't decode the image but we've received all the data, decoding 569 // has failed. 570 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 571 setFailed(); 572 // If we're done decoding the image, we don't need the PNGImageReader 573 // anymore. (If we failed, |m_reader| has already been cleared.) 574 else if (isComplete()) 575 m_reader.clear(); 576} 577 578} // namespace WebCore 579