| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006 Apple Computer, Inc. | 2 * Copyright (C) 2006 Apple Computer, Inc. |
| 3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. | 3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. |
| 4 * | 4 * |
| 5 * Portions are Copyright (C) 2001 mozilla.org | 5 * Portions are Copyright (C) 2001 mozilla.org |
| 6 * | 6 * |
| 7 * Other contributors: | 7 * Other contributors: |
| 8 * Stuart Parmenter <stuart@mozilla.com> | 8 * Stuart Parmenter <stuart@mozilla.com> |
| 9 * | 9 * |
| 10 * This library is free software; you can redistribute it and/or | 10 * This library is free software; you can redistribute it and/or |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 | 38 |
| 39 #include "platform/image-decoders/png/PNGImageDecoder.h" | 39 #include "platform/image-decoders/png/PNGImageDecoder.h" |
| 40 | 40 |
| 41 #include "png.h" | 41 #include "png.h" |
| 42 #include "wtf/PtrUtil.h" | 42 #include "wtf/PtrUtil.h" |
| 43 #include <memory> | 43 #include <memory> |
| 44 | 44 |
| 45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) | 45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) |
| 46 #error version error: compile against a versioned libpng. | 46 #error version error: compile against a versioned libpng. |
| 47 #endif | 47 #endif |
| 48 #if USE(QCMSLIB) | |
| 49 #include "qcms.h" | |
| 50 #endif | |
| 51 | 48 |
| 52 #if PNG_LIBPNG_VER_MAJOR > 1 || \ | 49 #if PNG_LIBPNG_VER_MAJOR > 1 || \ |
| 53 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) | 50 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) |
| 54 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) | 51 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) |
| 55 #else | 52 #else |
| 56 #define JMPBUF(png_ptr) png_ptr->jmpbuf | 53 #define JMPBUF(png_ptr) png_ptr->jmpbuf |
| 57 #endif | 54 #endif |
| 58 | 55 |
| 59 namespace { | 56 namespace { |
| 60 | 57 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 89 USING_FAST_MALLOC(PNGImageReader); | 86 USING_FAST_MALLOC(PNGImageReader); |
| 90 WTF_MAKE_NONCOPYABLE(PNGImageReader); | 87 WTF_MAKE_NONCOPYABLE(PNGImageReader); |
| 91 | 88 |
| 92 public: | 89 public: |
| 93 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) | 90 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) |
| 94 : m_decoder(decoder), | 91 : m_decoder(decoder), |
| 95 m_readOffset(readOffset), | 92 m_readOffset(readOffset), |
| 96 m_currentBufferSize(0), | 93 m_currentBufferSize(0), |
| 97 m_decodingSizeOnly(false), | 94 m_decodingSizeOnly(false), |
| 98 m_hasAlpha(false) | 95 m_hasAlpha(false) |
| 99 #if USE(QCMSLIB) | |
| 100 , | |
| 101 m_rowBuffer() | |
| 102 #endif | |
| 103 { | 96 { |
| 104 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); | 97 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
| 105 m_info = png_create_info_struct(m_png); | 98 m_info = png_create_info_struct(m_png); |
| 106 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, | 99 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, |
| 107 pngRowAvailable, pngComplete); | 100 pngRowAvailable, pngComplete); |
| 108 } | 101 } |
| 109 | 102 |
| 110 ~PNGImageReader() { | 103 ~PNGImageReader() { |
| 111 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); | 104 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); |
| 112 ASSERT(!m_png && !m_info); | 105 ASSERT(!m_png && !m_info); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 143 void setReadOffset(size_t offset) { m_readOffset = offset; } | 136 void setReadOffset(size_t offset) { m_readOffset = offset; } |
| 144 size_t currentBufferSize() const { return m_currentBufferSize; } | 137 size_t currentBufferSize() const { return m_currentBufferSize; } |
| 145 bool decodingSizeOnly() const { return m_decodingSizeOnly; } | 138 bool decodingSizeOnly() const { return m_decodingSizeOnly; } |
| 146 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } | 139 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } |
| 147 bool hasAlpha() const { return m_hasAlpha; } | 140 bool hasAlpha() const { return m_hasAlpha; } |
| 148 | 141 |
| 149 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } | 142 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } |
| 150 void createInterlaceBuffer(int size) { | 143 void createInterlaceBuffer(int size) { |
| 151 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]); | 144 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]); |
| 152 } | 145 } |
| 153 #if USE(QCMSLIB) | |
| 154 png_bytep rowBuffer() const { return m_rowBuffer.get(); } | |
| 155 void createRowBuffer(int size) { | |
| 156 m_rowBuffer = wrapArrayUnique(new png_byte[size]); | |
| 157 } | |
| 158 #endif | |
| 159 | 146 |
| 160 private: | 147 private: |
| 161 png_structp m_png; | 148 png_structp m_png; |
| 162 png_infop m_info; | 149 png_infop m_info; |
| 163 PNGImageDecoder* m_decoder; | 150 PNGImageDecoder* m_decoder; |
| 164 size_t m_readOffset; | 151 size_t m_readOffset; |
| 165 size_t m_currentBufferSize; | 152 size_t m_currentBufferSize; |
| 166 bool m_decodingSizeOnly; | 153 bool m_decodingSizeOnly; |
| 167 bool m_hasAlpha; | 154 bool m_hasAlpha; |
| 168 std::unique_ptr<png_byte[]> m_interlaceBuffer; | 155 std::unique_ptr<png_byte[]> m_interlaceBuffer; |
| 169 #if USE(QCMSLIB) | |
| 170 std::unique_ptr<png_byte[]> m_rowBuffer; | |
| 171 #endif | |
| 172 }; | 156 }; |
| 173 | 157 |
| 174 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, | 158 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
| 175 GammaAndColorProfileOption colorOptions, | 159 GammaAndColorProfileOption colorOptions, |
| 176 size_t maxDecodedBytes, | 160 size_t maxDecodedBytes, |
| 177 size_t offset) | 161 size_t offset) |
| 178 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), | 162 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), |
| 179 m_offset(offset) {} | 163 m_offset(offset) {} |
| 180 | 164 |
| 181 PNGImageDecoder::~PNGImageDecoder() {} | 165 PNGImageDecoder::~PNGImageDecoder() {} |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 224 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) | 208 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) |
| 225 png_set_gray_to_rgb(png); | 209 png_set_gray_to_rgb(png); |
| 226 | 210 |
| 227 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { | 211 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { |
| 228 // We only support color profiles for color PALETTE and RGB[A] PNG. | 212 // We only support color profiles for color PALETTE and RGB[A] PNG. |
| 229 // Supporting color profiles for gray-scale images is slightly tricky, at | 213 // Supporting color profiles for gray-scale images is slightly tricky, at |
| 230 // least using the CoreGraphics ICC library, because we expand gray-scale | 214 // least using the CoreGraphics ICC library, because we expand gray-scale |
| 231 // images to RGB but we do not similarly transform the color profile. We'd | 215 // images to RGB but we do not similarly transform the color profile. We'd |
| 232 // either need to transform the color profile or we'd need to decode into a | 216 // either need to transform the color profile or we'd need to decode into a |
| 233 // gray-scale image buffer and hand that to CoreGraphics. | 217 // gray-scale image buffer and hand that to CoreGraphics. |
| 234 bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; | |
| 235 #ifdef PNG_iCCP_SUPPORTED | 218 #ifdef PNG_iCCP_SUPPORTED |
| 236 if (png_get_valid(png, info, PNG_INFO_sRGB)) { | 219 if (png_get_valid(png, info, PNG_INFO_sRGB)) { |
| 237 setColorProfileAndComputeTransform(nullptr, 0, imageHasAlpha, | 220 setColorSpaceAndComputeTransform(nullptr, 0, true /* useSRGB */); |
| 238 true /* useSRGB */); | |
| 239 } else { | 221 } else { |
| 240 char* profileName = nullptr; | 222 char* profileName = nullptr; |
| 241 int compressionType = 0; | 223 int compressionType = 0; |
| 242 #if (PNG_LIBPNG_VER < 10500) | 224 #if (PNG_LIBPNG_VER < 10500) |
| 243 png_charp profile = nullptr; | 225 png_charp profile = nullptr; |
| 244 #else | 226 #else |
| 245 png_bytep profile = nullptr; | 227 png_bytep profile = nullptr; |
| 246 #endif | 228 #endif |
| 247 png_uint_32 profileLength = 0; | 229 png_uint_32 profileLength = 0; |
| 248 if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, | 230 if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, |
| 249 &profileLength)) { | 231 &profileLength)) { |
| 250 setColorProfileAndComputeTransform(reinterpret_cast<char*>(profile), | 232 setColorSpaceAndComputeTransform(reinterpret_cast<char*>(profile), |
| 251 profileLength, imageHasAlpha, | 233 profileLength, false /* useSRGB */); |
| 252 false /* useSRGB */); | |
| 253 } | 234 } |
| 254 } | 235 } |
| 255 #endif // PNG_iCCP_SUPPORTED | 236 #endif // PNG_iCCP_SUPPORTED |
| 256 } | 237 } |
| 257 | 238 |
| 258 if (!hasColorProfile()) { | 239 if (!hasColorProfile()) { |
| 259 // Deal with gamma and keep it under our control. | 240 // TODO (msarett): |
| 241 // Applying the transfer function (gamma) should be handled by |
| 242 // SkColorSpaceXform. Here we always convert to a transfer function that |
| 243 // is a 2.2 exponential. This is a little strange given that the dst |
| 244 // transfer function is not necessarily a 2.2 exponential. |
| 245 // TODO (msarett): |
| 246 // Often, PNGs that specify their transfer function with the gAMA tag will |
| 247 // also specify their gamut with the cHRM tag. We should read this tag |
| 248 // and do a full color space transformation if it is present. |
| 260 const double inverseGamma = 0.45455; | 249 const double inverseGamma = 0.45455; |
| 261 const double defaultGamma = 2.2; | 250 const double defaultGamma = 2.2; |
| 262 double gamma; | 251 double gamma; |
| 263 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { | 252 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { |
| 264 const double maxGamma = 21474.83; | 253 const double maxGamma = 21474.83; |
| 265 if ((gamma <= 0.0) || (gamma > maxGamma)) { | 254 if ((gamma <= 0.0) || (gamma > maxGamma)) { |
| 266 gamma = inverseGamma; | 255 gamma = inverseGamma; |
| 267 png_set_gAMA(png, info, gamma); | 256 png_set_gAMA(png, info, gamma); |
| 268 } | 257 } |
| 269 png_set_gamma(png, defaultGamma, gamma); | 258 png_set_gamma(png, defaultGamma, gamma); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 if (PNG_INTERLACE_ADAM7 == | 306 if (PNG_INTERLACE_ADAM7 == |
| 318 png_get_interlace_type(png, m_reader->infoPtr())) { | 307 png_get_interlace_type(png, m_reader->infoPtr())) { |
| 319 m_reader->createInterlaceBuffer(colorChannels * size().width() * | 308 m_reader->createInterlaceBuffer(colorChannels * size().width() * |
| 320 size().height()); | 309 size().height()); |
| 321 if (!m_reader->interlaceBuffer()) { | 310 if (!m_reader->interlaceBuffer()) { |
| 322 longjmp(JMPBUF(png), 1); | 311 longjmp(JMPBUF(png), 1); |
| 323 return; | 312 return; |
| 324 } | 313 } |
| 325 } | 314 } |
| 326 | 315 |
| 327 #if USE(QCMSLIB) | |
| 328 if (colorTransform()) { | |
| 329 m_reader->createRowBuffer(colorChannels * size().width()); | |
| 330 if (!m_reader->rowBuffer()) { | |
| 331 longjmp(JMPBUF(png), 1); | |
| 332 return; | |
| 333 } | |
| 334 } | |
| 335 #endif | |
| 336 buffer.setStatus(ImageFrame::FramePartial); | 316 buffer.setStatus(ImageFrame::FramePartial); |
| 337 buffer.setHasAlpha(false); | 317 buffer.setHasAlpha(false); |
| 338 | 318 |
| 339 // For PNGs, the frame always fills the entire image. | 319 // For PNGs, the frame always fills the entire image. |
| 340 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); | 320 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); |
| 341 } | 321 } |
| 342 | 322 |
| 343 /* libpng comments (here to explain what follows). | 323 /* libpng comments (here to explain what follows). |
| 344 * | 324 * |
| 345 * this function is called for every row in the image. If the | 325 * this function is called for every row in the image. If the |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 382 | 362 |
| 383 bool hasAlpha = m_reader->hasAlpha(); | 363 bool hasAlpha = m_reader->hasAlpha(); |
| 384 png_bytep row = rowBuffer; | 364 png_bytep row = rowBuffer; |
| 385 | 365 |
| 386 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { | 366 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
| 387 unsigned colorChannels = hasAlpha ? 4 : 3; | 367 unsigned colorChannels = hasAlpha ? 4 : 3; |
| 388 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); | 368 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
| 389 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); | 369 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
| 390 } | 370 } |
| 391 | 371 |
| 392 #if USE(QCMSLIB) | |
| 393 if (qcms_transform* transform = colorTransform()) { | |
| 394 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); | |
| 395 row = m_reader->rowBuffer(); | |
| 396 } | |
| 397 #endif | |
| 398 | |
| 399 // Write the decoded row pixels to the frame buffer. The repetitive | 372 // Write the decoded row pixels to the frame buffer. The repetitive |
| 400 // form of the row write loops is for speed. | 373 // form of the row write loops is for speed. |
| 401 ImageFrame::PixelData* address = buffer.getAddr(0, y); | 374 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); |
| 402 unsigned alphaMask = 255; | 375 unsigned alphaMask = 255; |
| 403 int width = size().width(); | 376 int width = size().width(); |
| 404 | 377 |
| 405 png_bytep pixel = row; | 378 png_bytep srcPtr = row; |
| 406 if (hasAlpha) { | 379 if (hasAlpha) { |
| 380 #if USE(SKCOLORXFORM) |
| 381 // Here we apply the color space transformation to the dst space. |
| 382 // It does not really make sense to transform to a gamma-encoded |
| 383 // space and then immediately after, perform a linear premultiply. |
| 384 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), |
| 385 // instructing SkColorSpaceXform to perform the linear premultiply |
| 386 // while the pixels are a linear space. |
| 387 // We cannot do this because when we apply the gamma encoding after |
| 388 // the premultiply, we will very likely end up with valid pixels |
| 389 // where R, G, and/or B are greater than A. The legacy drawing |
| 390 // pipeline does not know how to handle this. |
| 391 if (SkColorSpaceXform* xform = colorTransform()) { |
| 392 SkColorSpaceXform::ColorFormat colorFormat = |
| 393 SkColorSpaceXform::kRGBA_8888_ColorFormat; |
| 394 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), |
| 395 kUnpremul_SkAlphaType); |
| 396 srcPtr = (png_bytep)dstRow; |
| 397 } |
| 398 #endif |
| 399 |
| 407 if (buffer.premultiplyAlpha()) { | 400 if (buffer.premultiplyAlpha()) { |
| 408 for (int x = 0; x < width; ++x, pixel += 4) { | 401 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
| 409 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], | 402 dstPixel++, srcPtr += 4) { |
| 410 pixel[3]); | 403 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
| 411 alphaMask &= pixel[3]; | 404 srcPtr[3]); |
| 405 alphaMask &= srcPtr[3]; |
| 412 } | 406 } |
| 413 } else { | 407 } else { |
| 414 for (int x = 0; x < width; ++x, pixel += 4) { | 408 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
| 415 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); | 409 dstPixel++, srcPtr += 4) { |
| 416 alphaMask &= pixel[3]; | 410 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); |
| 411 alphaMask &= srcPtr[3]; |
| 417 } | 412 } |
| 418 } | 413 } |
| 419 } else { | 414 } else { |
| 420 for (int x = 0; x < width; ++x, pixel += 3) { | 415 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
| 421 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); | 416 dstPixel++, srcPtr += 3) { |
| 417 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); |
| 422 } | 418 } |
| 419 |
| 420 #if USE(SKCOLORXFORM) |
| 421 // We'll apply the color space xform to opaque pixels after they have been |
| 422 // written to the ImageFrame, purely because SkColorSpaceXform supports |
| 423 // RGBA (and not RGB). |
| 424 if (SkColorSpaceXform* xform = colorTransform()) { |
| 425 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, |
| 426 size().width(), kOpaque_SkAlphaType); |
| 427 } |
| 428 #endif |
| 423 } | 429 } |
| 424 | 430 |
| 425 if (alphaMask != 255 && !buffer.hasAlpha()) | 431 if (alphaMask != 255 && !buffer.hasAlpha()) |
| 426 buffer.setHasAlpha(true); | 432 buffer.setHasAlpha(true); |
| 427 | 433 |
| 428 buffer.setPixelsChanged(true); | 434 buffer.setPixelsChanged(true); |
| 429 } | 435 } |
| 430 | 436 |
| 431 void PNGImageDecoder::complete() { | 437 void PNGImageDecoder::complete() { |
| 432 if (m_frameBufferCache.isEmpty()) | 438 if (m_frameBufferCache.isEmpty()) |
| (...skipping 17 matching lines...) Expand all Loading... |
| 450 // has failed. | 456 // has failed. |
| 451 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) | 457 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) |
| 452 setFailed(); | 458 setFailed(); |
| 453 | 459 |
| 454 // If decoding is done or failed, we don't need the PNGImageReader anymore. | 460 // If decoding is done or failed, we don't need the PNGImageReader anymore. |
| 455 if (isComplete(this) || failed()) | 461 if (isComplete(this) || failed()) |
| 456 m_reader.reset(); | 462 m_reader.reset(); |
| 457 } | 463 } |
| 458 | 464 |
| 459 } // namespace blink | 465 } // namespace blink |
| OLD | NEW |