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 WebCore { 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 if (m_transform) 133 qcms_transform_release(m_transform); 134 m_transform = 0; 135#endif 136 delete[] m_interlaceBuffer; 137 m_interlaceBuffer = 0; 138 m_readOffset = 0; 139 } 140 141 bool decode(const SharedBuffer& data, bool sizeOnly) 142 { 143 m_decodingSizeOnly = sizeOnly; 144 PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png)); 145 146 // We need to do the setjmp here. Otherwise bad things will happen. 147 if (setjmp(JMPBUF(m_png))) 148 return decoder->setFailed(); 149 150 const char* segment; 151 while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) { 152 m_readOffset += segmentLength; 153 m_currentBufferSize = m_readOffset; 154 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength); 155 // We explicitly specify the superclass isSizeAvailable() because we 156 // merely want to check if we've managed to set the size, not 157 // (recursively) trigger additional decoding if we haven't. 158 if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isComplete()) 159 return true; 160 } 161 return false; 162 } 163 164 png_structp pngPtr() const { return m_png; } 165 png_infop infoPtr() const { return m_info; } 166 167 void setReadOffset(unsigned offset) { m_readOffset = offset; } 168 unsigned currentBufferSize() const { return m_currentBufferSize; } 169 bool decodingSizeOnly() const { return m_decodingSizeOnly; } 170 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } 171 bool hasAlpha() const { return m_hasAlpha; } 172 173 png_bytep interlaceBuffer() const { return m_interlaceBuffer; } 174 void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; } 175#if USE(QCMSLIB) 176 png_bytep rowBuffer() const { return m_rowBuffer.get(); } 177 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[size]); } 178 qcms_transform* colorTransform() const { return m_transform; } 179 180 void createColorTransform(const ColorProfile& colorProfile, bool hasAlpha) 181 { 182 if (m_transform) 183 qcms_transform_release(m_transform); 184 m_transform = 0; 185 186 if (colorProfile.isEmpty()) 187 return; 188 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); 189 if (!deviceProfile) 190 return; 191 qcms_profile* inputProfile = qcms_profile_from_memory(colorProfile.data(), colorProfile.size()); 192 if (!inputProfile) 193 return; 194 // We currently only support color profiles for RGB and RGBA images. 195 ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); 196 qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_8; 197 // FIXME: Don't force perceptual intent if the image profile contains an intent. 198 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProfile, dataFormat, QCMS_INTENT_PERCEPTUAL); 199 qcms_profile_release(inputProfile); 200 } 201#endif 202 203private: 204 png_structp m_png; 205 png_infop m_info; 206 unsigned m_readOffset; 207 unsigned m_currentBufferSize; 208 bool m_decodingSizeOnly; 209 bool m_hasAlpha; 210 png_bytep m_interlaceBuffer; 211#if USE(QCMSLIB) 212 qcms_transform* m_transform; 213 OwnPtr<png_byte[]> m_rowBuffer; 214#endif 215}; 216 217PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, 218 ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption, 219 size_t maxDecodedBytes) 220 : ImageDecoder(alphaOption, gammaAndColorProfileOption, maxDecodedBytes) 221 , m_doNothingOnFailure(false) 222{ 223} 224 225PNGImageDecoder::~PNGImageDecoder() 226{ 227} 228 229bool PNGImageDecoder::isSizeAvailable() 230{ 231 if (!ImageDecoder::isSizeAvailable()) 232 decode(true); 233 234 return ImageDecoder::isSizeAvailable(); 235} 236 237ImageFrame* PNGImageDecoder::frameBufferAtIndex(size_t index) 238{ 239 if (index) 240 return 0; 241 242 if (m_frameBufferCache.isEmpty()) { 243 m_frameBufferCache.resize(1); 244 m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); 245 } 246 247 ImageFrame& frame = m_frameBufferCache[0]; 248 if (frame.status() != ImageFrame::FrameComplete) { 249 PlatformInstrumentation::willDecodeImage("PNG"); 250 decode(false); 251 PlatformInstrumentation::didDecodeImage(); 252 } 253 254 frame.notifyBitmapIfPixelsChanged(); 255 return &frame; 256} 257 258bool PNGImageDecoder::setFailed() 259{ 260 if (m_doNothingOnFailure) 261 return false; 262 m_reader.clear(); 263 return ImageDecoder::setFailed(); 264} 265 266#if USE(QCMSLIB) 267static void readColorProfile(png_structp png, png_infop info, ColorProfile& colorProfile) 268{ 269#ifdef PNG_iCCP_SUPPORTED 270 char* profileName; 271 int compressionType; 272#if (PNG_LIBPNG_VER < 10500) 273 png_charp profile; 274#else 275 png_bytep profile; 276#endif 277 png_uint_32 profileLength; 278 if (!png_get_iCCP(png, info, &profileName, &compressionType, &profile, &profileLength)) 279 return; 280 281 // Only accept RGB color profiles from input class devices. 282 bool ignoreProfile = false; 283 char* profileData = reinterpret_cast<char*>(profile); 284 if (profileLength < ImageDecoder::iccColorProfileHeaderLength) 285 ignoreProfile = true; 286 else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) 287 ignoreProfile = true; 288 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) 289 ignoreProfile = true; 290 291 ASSERT(colorProfile.isEmpty()); 292 if (!ignoreProfile) 293 colorProfile.append(profileData, profileLength); 294#endif 295} 296#endif 297 298void PNGImageDecoder::headerAvailable() 299{ 300 png_structp png = m_reader->pngPtr(); 301 png_infop info = m_reader->infoPtr(); 302 png_uint_32 width = png_get_image_width(png, info); 303 png_uint_32 height = png_get_image_height(png, info); 304 305 // Protect against large images. 306 if (width > cMaxPNGSize || height > cMaxPNGSize) { 307 longjmp(JMPBUF(png), 1); 308 return; 309 } 310 311 // We can fill in the size now that the header is available. Avoid memory 312 // corruption issues by neutering setFailed() during this call; if we don't 313 // do this, failures will cause |m_reader| to be deleted, and our jmpbuf 314 // will cease to exist. Note that we'll still properly set the failure flag 315 // in this case as soon as we longjmp(). 316 m_doNothingOnFailure = true; 317 bool result = setSize(width, height); 318 m_doNothingOnFailure = false; 319 if (!result) { 320 longjmp(JMPBUF(png), 1); 321 return; 322 } 323 324 int bitDepth, colorType, interlaceType, compressionType, filterType, channels; 325 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); 326 327 // The options we set here match what Mozilla does. 328 329 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 330 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 331 png_set_expand(png); 332 333 png_bytep trns = 0; 334 int trnsCount = 0; 335 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 336 png_get_tRNS(png, info, &trns, &trnsCount, 0); 337 png_set_expand(png); 338 } 339 340 if (bitDepth == 16) 341 png_set_strip_16(png); 342 343 if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 344 png_set_gray_to_rgb(png); 345 346#if USE(QCMSLIB) 347 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { 348 // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting 349 // color profiles for gray-scale images is slightly tricky, at least using the 350 // CoreGraphics ICC library, because we expand gray-scale images to RGB but we 351 // do not similarly transform the color profile. We'd either need to transform 352 // the color profile or we'd need to decode into a gray-scale image buffer and 353 // hand that to CoreGraphics. 354 ColorProfile colorProfile; 355 readColorProfile(png, info, colorProfile); 356 bool decodedImageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; 357 m_reader->createColorTransform(colorProfile, decodedImageHasAlpha); 358 } 359#endif 360 361 // Deal with gamma and keep it under our control. 362 double gamma; 363 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { 364 if ((gamma <= 0.0) || (gamma > cMaxGamma)) { 365 gamma = cInverseGamma; 366 png_set_gAMA(png, info, gamma); 367 } 368 png_set_gamma(png, cDefaultGamma, gamma); 369 } else 370 png_set_gamma(png, cDefaultGamma, cInverseGamma); 371 372 // Tell libpng to send us rows for interlaced pngs. 373 if (interlaceType == PNG_INTERLACE_ADAM7) 374 png_set_interlace_handling(png); 375 376 // Update our info now. 377 png_read_update_info(png, info); 378 channels = png_get_channels(png, info); 379 ASSERT(channels == 3 || channels == 4); 380 381 m_reader->setHasAlpha(channels == 4); 382 383 if (m_reader->decodingSizeOnly()) { 384 // If we only needed the size, halt the reader. 385#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)) 386 // '0' argument to png_process_data_pause means: Do not cache unprocessed data. 387 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0)); 388#else 389 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); 390 png->buffer_size = 0; 391#endif 392 } 393} 394 395void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) 396{ 397 if (m_frameBufferCache.isEmpty()) 398 return; 399 400 // Initialize the framebuffer if needed. 401 ImageFrame& buffer = m_frameBufferCache[0]; 402 if (buffer.status() == ImageFrame::FrameEmpty) { 403 png_structp png = m_reader->pngPtr(); 404 if (!buffer.setSize(size().width(), size().height())) { 405 longjmp(JMPBUF(png), 1); 406 return; 407 } 408 409 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 410 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { 411 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); 412 if (!m_reader->interlaceBuffer()) { 413 longjmp(JMPBUF(png), 1); 414 return; 415 } 416 } 417 418#if USE(QCMSLIB) 419 if (m_reader->colorTransform()) { 420 m_reader->createRowBuffer(colorChannels * size().width()); 421 if (!m_reader->rowBuffer()) { 422 longjmp(JMPBUF(png), 1); 423 return; 424 } 425 } 426#endif 427 buffer.setStatus(ImageFrame::FramePartial); 428 buffer.setHasAlpha(false); 429 430 // For PNGs, the frame always fills the entire image. 431 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); 432 } 433 434 /* libpng comments (here to explain what follows). 435 * 436 * this function is called for every row in the image. If the 437 * image is interlacing, and you turned on the interlace handler, 438 * this function will be called for every row in every pass. 439 * Some of these rows will not be changed from the previous pass. 440 * When the row is not changed, the new_row variable will be NULL. 441 * The rows and passes are called in order, so you don't really 442 * need the row_num and pass, but I'm supplying them because it 443 * may make your life easier. 444 */ 445 446 // Nothing to do if the row is unchanged, or the row is outside 447 // the image bounds: libpng may send extra rows, ignore them to 448 // make our lives easier. 449 if (!rowBuffer) 450 return; 451 int y = rowIndex; 452 if (y < 0 || y >= size().height()) 453 return; 454 455 /* libpng comments (continued). 456 * 457 * For the non-NULL rows of interlaced images, you must call 458 * png_progressive_combine_row() passing in the row and the 459 * old row. You can call this function for NULL rows (it will 460 * just return) and for non-interlaced images (it just does the 461 * memcpy for you) if it will make the code easier. Thus, you 462 * can just do this for all cases: 463 * 464 * png_progressive_combine_row(png_ptr, old_row, new_row); 465 * 466 * where old_row is what was displayed for previous rows. Note 467 * that the first pass (pass == 0 really) will completely cover 468 * the old row, so the rows do not have to be initialized. After 469 * the first pass (and only for interlaced images), you will have 470 * to pass the current row, and the function will combine the 471 * old row and the new row. 472 */ 473 474 bool hasAlpha = m_reader->hasAlpha(); 475 unsigned colorChannels = hasAlpha ? 4 : 3; 476 png_bytep row = rowBuffer; 477 478 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 479 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 480 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 481 } 482 483#if USE(QCMSLIB) 484 if (qcms_transform* transform = m_reader->colorTransform()) { 485 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); 486 row = m_reader->rowBuffer(); 487 } 488#endif 489 490 // Write the decoded row pixels to the frame buffer. 491 ImageFrame::PixelData* address = buffer.getAddr(0, y); 492 bool nonTrivialAlpha = false; 493 int width = size().width(); 494 495 png_bytep pixel = row; 496 for (int x = 0; x < width; ++x, pixel += colorChannels) { 497 unsigned alpha = hasAlpha ? pixel[3] : 255; 498 buffer.setRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); 499 nonTrivialAlpha |= alpha < 255; 500 } 501 502 if (nonTrivialAlpha && !buffer.hasAlpha()) 503 buffer.setHasAlpha(nonTrivialAlpha); 504 505 buffer.setPixelsChanged(true); 506} 507 508void PNGImageDecoder::pngComplete() 509{ 510 if (!m_frameBufferCache.isEmpty()) 511 m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete); 512} 513 514void PNGImageDecoder::decode(bool onlySize) 515{ 516 if (failed()) 517 return; 518 519 if (!m_reader) 520 m_reader = adoptPtr(new PNGImageReader(this)); 521 522 // If we couldn't decode the image but we've received all the data, decoding 523 // has failed. 524 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 525 setFailed(); 526 // If we're done decoding the image, we don't need the PNGImageReader 527 // anymore. (If we failed, |m_reader| has already been cleared.) 528 else if (isComplete()) 529 m_reader.clear(); 530} 531 532} // namespace WebCore 533