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 (at) 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 56 namespace WebCore { 57 58 // Gamma constants. 59 const double cMaxGamma = 21474.83; 60 const double cDefaultGamma = 2.2; 61 const double cInverseGamma = 0.45455; 62 63 // Protect against large PNGs. See Mozilla's bug #251381 for more info. 64 const unsigned long cMaxPNGSize = 1000000UL; 65 66 // Called if the decoding of the image fails. 67 static 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). 75 static 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). 85 static 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. 91 static 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. 97 static void PNGAPI pngComplete(png_structp png, png_infop) 98 { 99 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); 100 } 101 102 class PNGImageReader { 103 WTF_MAKE_FAST_ALLOCATED; 104 public: 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 203 private: 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 217 PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, 218 ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption, 219 size_t maxDecodedBytes) 220 : ImageDecoder(alphaOption, gammaAndColorProfileOption, maxDecodedBytes) 221 , m_doNothingOnFailure(false) 222 , m_hasColorProfile(false) 223 { 224 } 225 226 PNGImageDecoder::~PNGImageDecoder() 227 { 228 } 229 230 bool PNGImageDecoder::isSizeAvailable() 231 { 232 if (!ImageDecoder::isSizeAvailable()) 233 decode(true); 234 235 return ImageDecoder::isSizeAvailable(); 236 } 237 238 ImageFrame* PNGImageDecoder::frameBufferAtIndex(size_t index) 239 { 240 if (index) 241 return 0; 242 243 if (m_frameBufferCache.isEmpty()) { 244 m_frameBufferCache.resize(1); 245 m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); 246 } 247 248 ImageFrame& frame = m_frameBufferCache[0]; 249 if (frame.status() != ImageFrame::FrameComplete) { 250 PlatformInstrumentation::willDecodeImage("PNG"); 251 decode(false); 252 PlatformInstrumentation::didDecodeImage(); 253 } 254 255 frame.notifyBitmapIfPixelsChanged(); 256 return &frame; 257 } 258 259 bool PNGImageDecoder::setFailed() 260 { 261 if (m_doNothingOnFailure) 262 return false; 263 m_reader.clear(); 264 return ImageDecoder::setFailed(); 265 } 266 267 #if USE(QCMSLIB) 268 static void readColorProfile(png_structp png, png_infop info, ColorProfile& colorProfile) 269 { 270 #ifdef PNG_iCCP_SUPPORTED 271 char* profileName; 272 int compressionType; 273 #if (PNG_LIBPNG_VER < 10500) 274 png_charp profile; 275 #else 276 png_bytep profile; 277 #endif 278 png_uint_32 profileLength; 279 if (!png_get_iCCP(png, info, &profileName, &compressionType, &profile, &profileLength)) 280 return; 281 282 // Only accept RGB color profiles from input class devices. 283 bool ignoreProfile = false; 284 char* profileData = reinterpret_cast<char*>(profile); 285 if (profileLength < ImageDecoder::iccColorProfileHeaderLength) 286 ignoreProfile = true; 287 else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) 288 ignoreProfile = true; 289 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) 290 ignoreProfile = true; 291 292 ASSERT(colorProfile.isEmpty()); 293 if (!ignoreProfile) 294 colorProfile.append(profileData, profileLength); 295 #endif 296 } 297 #endif 298 299 void PNGImageDecoder::headerAvailable() 300 { 301 png_structp png = m_reader->pngPtr(); 302 png_infop info = m_reader->infoPtr(); 303 png_uint_32 width = png_get_image_width(png, info); 304 png_uint_32 height = png_get_image_height(png, info); 305 306 // Protect against large images. 307 if (width > cMaxPNGSize || height > cMaxPNGSize) { 308 longjmp(JMPBUF(png), 1); 309 return; 310 } 311 312 // We can fill in the size now that the header is available. Avoid memory 313 // corruption issues by neutering setFailed() during this call; if we don't 314 // do this, failures will cause |m_reader| to be deleted, and our jmpbuf 315 // will cease to exist. Note that we'll still properly set the failure flag 316 // in this case as soon as we longjmp(). 317 m_doNothingOnFailure = true; 318 bool result = setSize(width, height); 319 m_doNothingOnFailure = false; 320 if (!result) { 321 longjmp(JMPBUF(png), 1); 322 return; 323 } 324 325 int bitDepth, colorType, interlaceType, compressionType, filterType, channels; 326 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); 327 328 // The options we set here match what Mozilla does. 329 330 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 331 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 332 png_set_expand(png); 333 334 png_bytep trns = 0; 335 int trnsCount = 0; 336 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 337 png_get_tRNS(png, info, &trns, &trnsCount, 0); 338 png_set_expand(png); 339 } 340 341 if (bitDepth == 16) 342 png_set_strip_16(png); 343 344 if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 345 png_set_gray_to_rgb(png); 346 347 #if USE(QCMSLIB) 348 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { 349 // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting 350 // color profiles for gray-scale images is slightly tricky, at least using the 351 // CoreGraphics ICC library, because we expand gray-scale images to RGB but we 352 // do not similarly transform the color profile. We'd either need to transform 353 // the color profile or we'd need to decode into a gray-scale image buffer and 354 // hand that to CoreGraphics. 355 ColorProfile colorProfile; 356 readColorProfile(png, info, colorProfile); 357 bool decodedImageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; 358 m_reader->createColorTransform(colorProfile, decodedImageHasAlpha); 359 m_hasColorProfile = !!m_reader->colorTransform(); 360 } 361 #endif 362 363 // Deal with gamma and keep it under our control. 364 double gamma; 365 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { 366 if ((gamma <= 0.0) || (gamma > cMaxGamma)) { 367 gamma = cInverseGamma; 368 png_set_gAMA(png, info, gamma); 369 } 370 png_set_gamma(png, cDefaultGamma, gamma); 371 } else 372 png_set_gamma(png, cDefaultGamma, cInverseGamma); 373 374 // Tell libpng to send us rows for interlaced pngs. 375 if (interlaceType == PNG_INTERLACE_ADAM7) 376 png_set_interlace_handling(png); 377 378 // Update our info now. 379 png_read_update_info(png, info); 380 channels = png_get_channels(png, info); 381 ASSERT(channels == 3 || channels == 4); 382 383 m_reader->setHasAlpha(channels == 4); 384 385 if (m_reader->decodingSizeOnly()) { 386 // If we only needed the size, halt the reader. 387 #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)) 388 // '0' argument to png_process_data_pause means: Do not cache unprocessed data. 389 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0)); 390 #else 391 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); 392 png->buffer_size = 0; 393 #endif 394 } 395 } 396 397 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) 398 { 399 if (m_frameBufferCache.isEmpty()) 400 return; 401 402 // Initialize the framebuffer if needed. 403 ImageFrame& buffer = m_frameBufferCache[0]; 404 if (buffer.status() == ImageFrame::FrameEmpty) { 405 png_structp png = m_reader->pngPtr(); 406 if (!buffer.setSize(size().width(), size().height())) { 407 longjmp(JMPBUF(png), 1); 408 return; 409 } 410 411 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 412 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { 413 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); 414 if (!m_reader->interlaceBuffer()) { 415 longjmp(JMPBUF(png), 1); 416 return; 417 } 418 } 419 420 #if USE(QCMSLIB) 421 if (m_reader->colorTransform()) { 422 m_reader->createRowBuffer(colorChannels * size().width()); 423 if (!m_reader->rowBuffer()) { 424 longjmp(JMPBUF(png), 1); 425 return; 426 } 427 } 428 #endif 429 buffer.setStatus(ImageFrame::FramePartial); 430 buffer.setHasAlpha(false); 431 432 // For PNGs, the frame always fills the entire image. 433 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); 434 } 435 436 /* libpng comments (here to explain what follows). 437 * 438 * this function is called for every row in the image. If the 439 * image is interlacing, and you turned on the interlace handler, 440 * this function will be called for every row in every pass. 441 * Some of these rows will not be changed from the previous pass. 442 * When the row is not changed, the new_row variable will be NULL. 443 * The rows and passes are called in order, so you don't really 444 * need the row_num and pass, but I'm supplying them because it 445 * may make your life easier. 446 */ 447 448 // Nothing to do if the row is unchanged, or the row is outside 449 // the image bounds: libpng may send extra rows, ignore them to 450 // make our lives easier. 451 if (!rowBuffer) 452 return; 453 int y = rowIndex; 454 if (y < 0 || y >= size().height()) 455 return; 456 457 /* libpng comments (continued). 458 * 459 * For the non-NULL rows of interlaced images, you must call 460 * png_progressive_combine_row() passing in the row and the 461 * old row. You can call this function for NULL rows (it will 462 * just return) and for non-interlaced images (it just does the 463 * memcpy for you) if it will make the code easier. Thus, you 464 * can just do this for all cases: 465 * 466 * png_progressive_combine_row(png_ptr, old_row, new_row); 467 * 468 * where old_row is what was displayed for previous rows. Note 469 * that the first pass (pass == 0 really) will completely cover 470 * the old row, so the rows do not have to be initialized. After 471 * the first pass (and only for interlaced images), you will have 472 * to pass the current row, and the function will combine the 473 * old row and the new row. 474 */ 475 476 bool hasAlpha = m_reader->hasAlpha(); 477 unsigned colorChannels = hasAlpha ? 4 : 3; 478 png_bytep row = rowBuffer; 479 480 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 481 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 482 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 483 } 484 485 #if USE(QCMSLIB) 486 if (qcms_transform* transform = m_reader->colorTransform()) { 487 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); 488 row = m_reader->rowBuffer(); 489 } 490 #endif 491 492 // Write the decoded row pixels to the frame buffer. The repetitive 493 // form of the row write loops is for speed. 494 ImageFrame::PixelData* address = buffer.getAddr(0, y); 495 unsigned alphaMask = 255; 496 int width = size().width(); 497 498 png_bytep pixel = row; 499 if (hasAlpha) { 500 if (buffer.premultiplyAlpha()) { 501 for (int x = 0; x < width; ++x, pixel += 4) { 502 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]); 503 alphaMask &= pixel[3]; 504 } 505 } else { 506 for (int x = 0; x < width; ++x, pixel += 4) { 507 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); 508 alphaMask &= pixel[3]; 509 } 510 } 511 } else { 512 for (int x = 0; x < width; ++x, pixel += 3) { 513 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); 514 } 515 } 516 517 if (alphaMask != 255 && !buffer.hasAlpha()) 518 buffer.setHasAlpha(true); 519 520 buffer.setPixelsChanged(true); 521 } 522 523 void PNGImageDecoder::pngComplete() 524 { 525 if (!m_frameBufferCache.isEmpty()) 526 m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete); 527 } 528 529 void PNGImageDecoder::decode(bool onlySize) 530 { 531 if (failed()) 532 return; 533 534 if (!m_reader) 535 m_reader = adoptPtr(new PNGImageReader(this)); 536 537 // If we couldn't decode the image but we've received all the data, decoding 538 // has failed. 539 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 540 setFailed(); 541 // If we're done decoding the image, we don't need the PNGImageReader 542 // anymore. (If we failed, |m_reader| has already been cleared.) 543 else if (isComplete()) 544 m_reader.clear(); 545 } 546 547 } // namespace WebCore 548