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