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 "core/platform/image-decoders/png/PNGImageDecoder.h" 41 42 #include "core/platform/PlatformInstrumentation.h" 43 #include "wtf/OwnArrayPtr.h" 44 #include "wtf/PassOwnPtr.h" 45 46 #include "png.h" 47 #if USE(QCMSLIB) 48 #include "qcms.h" 49 #endif 50 51 #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)) 52 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) 53 #else 54 #define JMPBUF(png_ptr) png_ptr->jmpbuf 55 #endif 56 57 namespace WebCore { 58 59 // Gamma constants. 60 const double cMaxGamma = 21474.83; 61 const double cDefaultGamma = 2.2; 62 const double cInverseGamma = 0.45455; 63 64 // Protect against large PNGs. See Mozilla's bug #251381 for more info. 65 const unsigned long cMaxPNGSize = 1000000UL; 66 67 // Called if the decoding of the image fails. 68 static void PNGAPI decodingFailed(png_structp png, png_const_charp) 69 { 70 longjmp(JMPBUF(png), 1); 71 } 72 73 // Callbacks given to the read struct. The first is for warnings (we want to 74 // treat a particular warning as an error, which is why we have to register this 75 // callback). 76 static void PNGAPI decodingWarning(png_structp png, png_const_charp warningMsg) 77 { 78 // Mozilla did this, so we will too. 79 // Convert a tRNS warning to be an error (see 80 // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 ) 81 if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24)) 82 png_error(png, warningMsg); 83 } 84 85 // Called when we have obtained the header information (including the size). 86 static void PNGAPI headerAvailable(png_structp png, png_infop) 87 { 88 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); 89 } 90 91 // Called when a row is ready. 92 static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass) 93 { 94 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); 95 } 96 97 // Called when we have completely finished decoding the image. 98 static void PNGAPI pngComplete(png_structp png, png_infop) 99 { 100 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); 101 } 102 103 class PNGImageReader { 104 WTF_MAKE_FAST_ALLOCATED; 105 public: 106 PNGImageReader(PNGImageDecoder* decoder) 107 : m_readOffset(0) 108 , m_currentBufferSize(0) 109 , m_decodingSizeOnly(false) 110 , m_hasAlpha(false) 111 , m_interlaceBuffer(0) 112 #if USE(QCMSLIB) 113 , m_transform(0) 114 , m_rowBuffer() 115 #endif 116 { 117 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning); 118 m_info = png_create_info_struct(m_png); 119 png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete); 120 } 121 122 ~PNGImageReader() 123 { 124 close(); 125 } 126 127 void close() 128 { 129 if (m_png && m_info) 130 // This will zero the pointers. 131 png_destroy_read_struct(&m_png, &m_info, 0); 132 #if USE(QCMSLIB) 133 if (m_transform) 134 qcms_transform_release(m_transform); 135 m_transform = 0; 136 #endif 137 delete[] m_interlaceBuffer; 138 m_interlaceBuffer = 0; 139 m_readOffset = 0; 140 } 141 142 bool decode(const SharedBuffer& data, bool sizeOnly) 143 { 144 m_decodingSizeOnly = sizeOnly; 145 PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png)); 146 147 // We need to do the setjmp here. Otherwise bad things will happen. 148 if (setjmp(JMPBUF(m_png))) 149 return decoder->setFailed(); 150 151 const char* segment; 152 while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) { 153 m_readOffset += segmentLength; 154 m_currentBufferSize = m_readOffset; 155 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength); 156 // We explicitly specify the superclass isSizeAvailable() because we 157 // merely want to check if we've managed to set the size, not 158 // (recursively) trigger additional decoding if we haven't. 159 if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isComplete()) 160 return true; 161 } 162 return false; 163 } 164 165 png_structp pngPtr() const { return m_png; } 166 png_infop infoPtr() const { return m_info; } 167 168 void setReadOffset(unsigned offset) { m_readOffset = offset; } 169 unsigned currentBufferSize() const { return m_currentBufferSize; } 170 bool decodingSizeOnly() const { return m_decodingSizeOnly; } 171 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } 172 bool hasAlpha() const { return m_hasAlpha; } 173 174 png_bytep interlaceBuffer() const { return m_interlaceBuffer; } 175 void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; } 176 #if USE(QCMSLIB) 177 png_bytep rowBuffer() const { return m_rowBuffer.get(); } 178 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[size]); } 179 qcms_transform* colorTransform() const { return m_transform; } 180 181 void createColorTransform(const ColorProfile& colorProfile, bool hasAlpha) 182 { 183 if (m_transform) 184 qcms_transform_release(m_transform); 185 m_transform = 0; 186 187 if (colorProfile.isEmpty()) 188 return; 189 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); 190 if (!deviceProfile) 191 return; 192 qcms_profile* inputProfile = qcms_profile_from_memory(colorProfile.data(), colorProfile.size()); 193 if (!inputProfile) 194 return; 195 // We currently only support color profiles for RGB and RGBA images. 196 ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); 197 qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_8; 198 // FIXME: Don't force perceptual intent if the image profile contains an intent. 199 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProfile, dataFormat, QCMS_INTENT_PERCEPTUAL); 200 qcms_profile_release(inputProfile); 201 } 202 #endif 203 204 private: 205 png_structp m_png; 206 png_infop m_info; 207 unsigned m_readOffset; 208 unsigned m_currentBufferSize; 209 bool m_decodingSizeOnly; 210 bool m_hasAlpha; 211 png_bytep m_interlaceBuffer; 212 #if USE(QCMSLIB) 213 qcms_transform* m_transform; 214 OwnArrayPtr<png_byte> m_rowBuffer; 215 #endif 216 }; 217 218 PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, 219 ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) 220 : ImageDecoder(alphaOption, gammaAndColorProfileOption) 221 , m_doNothingOnFailure(false) 222 { 223 } 224 225 PNGImageDecoder::~PNGImageDecoder() 226 { 227 } 228 229 bool PNGImageDecoder::isSizeAvailable() 230 { 231 if (!ImageDecoder::isSizeAvailable()) 232 decode(true); 233 234 return ImageDecoder::isSizeAvailable(); 235 } 236 237 ImageFrame* 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 return &frame; 254 } 255 256 bool PNGImageDecoder::setFailed() 257 { 258 if (m_doNothingOnFailure) 259 return false; 260 m_reader.clear(); 261 return ImageDecoder::setFailed(); 262 } 263 264 static void readColorProfile(png_structp png, png_infop info, ColorProfile& colorProfile) 265 { 266 #ifdef PNG_iCCP_SUPPORTED 267 char* profileName; 268 int compressionType; 269 #if (PNG_LIBPNG_VER < 10500) 270 png_charp profile; 271 #else 272 png_bytep profile; 273 #endif 274 png_uint_32 profileLength; 275 if (!png_get_iCCP(png, info, &profileName, &compressionType, &profile, &profileLength)) 276 return; 277 278 // Only accept RGB color profiles from input class devices. 279 bool ignoreProfile = false; 280 char* profileData = reinterpret_cast<char*>(profile); 281 if (profileLength < ImageDecoder::iccColorProfileHeaderLength) 282 ignoreProfile = true; 283 else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) 284 ignoreProfile = true; 285 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) 286 ignoreProfile = true; 287 288 ASSERT(colorProfile.isEmpty()); 289 if (!ignoreProfile) 290 colorProfile.append(profileData, profileLength); 291 #else 292 UNUSED_PARAM(png); 293 UNUSED_PARAM(info); 294 UNUSED_PARAM(colorProfile); 295 #endif 296 } 297 298 void 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 395 void 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 506 void PNGImageDecoder::pngComplete() 507 { 508 if (!m_frameBufferCache.isEmpty()) 509 m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete); 510 } 511 512 void PNGImageDecoder::decode(bool onlySize) 513 { 514 if (failed()) 515 return; 516 517 if (!m_reader) 518 m_reader = adoptPtr(new PNGImageReader(this)); 519 520 // If we couldn't decode the image but we've received all the data, decoding 521 // has failed. 522 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 523 setFailed(); 524 // If we're done decoding the image, we don't need the PNGImageReader 525 // anymore. (If we failed, |m_reader| has already been cleared.) 526 else if (isComplete()) 527 m_reader.clear(); 528 } 529 530 } // namespace WebCore 531