| 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 21 matching lines...) Expand all Loading... |
| 32 * version of this file under the LGPL, indicate your decision by | 32 * version of this file under the LGPL, indicate your decision by |
| 33 * deletingthe provisions above and replace them with the notice and | 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. | 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 | 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. | 36 * version of this file under any of the LGPL, the MPL or the GPL. |
| 37 */ | 37 */ |
| 38 | 38 |
| 39 #include "config.h" | 39 #include "config.h" |
| 40 #include "platform/image-decoders/png/PNGImageDecoder.h" | 40 #include "platform/image-decoders/png/PNGImageDecoder.h" |
| 41 | 41 |
| 42 #include "platform/graphics/GraphicsScreen.h" |
| 43 |
| 42 #include "png.h" | 44 #include "png.h" |
| 43 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) | 45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) |
| 44 #error version error: compile against a versioned libpng. | 46 #error version error: compile against a versioned libpng. |
| 45 #endif | 47 #endif |
| 46 #if USE(QCMSLIB) | 48 #if USE(QCMSLIB) |
| 47 #include "qcms.h" | 49 #include "qcms.h" |
| 48 #endif | 50 #endif |
| 49 | 51 |
| 50 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MIN
OR >= 4) | 52 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MIN
OR >= 4) |
| 51 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) | 53 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[si
ze]); } | 152 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[si
ze]); } |
| 151 qcms_transform* colorTransform() const { return m_transform; } | 153 qcms_transform* colorTransform() const { return m_transform; } |
| 152 | 154 |
| 153 void clearColorTransform() | 155 void clearColorTransform() |
| 154 { | 156 { |
| 155 if (m_transform) | 157 if (m_transform) |
| 156 qcms_transform_release(m_transform); | 158 qcms_transform_release(m_transform); |
| 157 m_transform = 0; | 159 m_transform = 0; |
| 158 } | 160 } |
| 159 | 161 |
| 160 void createColorTransform(const ColorProfile& colorProfile, bool hasAlpha, b
ool sRGB) | 162 PassRefPtr<ColorSpaceProfile> createColorTransform(const ColorProfile& color
Profile, bool hasAlpha, bool sRGB) |
| 161 { | 163 { |
| 162 clearColorTransform(); | 164 clearColorTransform(); |
| 163 | 165 |
| 166 fprintf(stderr, "PNG decoder %p createColorTransform ", m_decoder); |
| 167 if (m_decoder->deviceProfile()) |
| 168 fprintf(stderr, ": device %p\n", m_decoder->deviceProfile().get()); |
| 169 else |
| 170 fprintf(stderr, ": %p\n", nullptr); |
| 171 fprintf(stderr, "image color profiles enabled: %d\n", imageColorProfiles
Enabled()); |
| 172 fflush(stderr); |
| 173 |
| 164 if (colorProfile.isEmpty() && !sRGB) | 174 if (colorProfile.isEmpty() && !sRGB) |
| 165 return; | 175 return nullptr; |
| 176 |
| 166 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); | 177 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); |
| 178 if (m_decoder->deviceProfile()) |
| 179 deviceProfile = m_decoder->deviceProfile()->profile(); |
| 167 if (!deviceProfile) | 180 if (!deviceProfile) |
| 168 return; | 181 return nullptr; |
| 169 qcms_profile* inputProfile = 0; | 182 |
| 183 qcms_profile* inputProfile = nullptr; |
| 170 if (!colorProfile.isEmpty()) | 184 if (!colorProfile.isEmpty()) |
| 171 inputProfile = qcms_profile_from_memory(colorProfile.data(), colorPr
ofile.size()); | 185 inputProfile = qcms_profile_from_memory(colorProfile.data(), colorPr
ofile.size()); |
| 172 else | 186 else |
| 173 inputProfile = qcms_profile_sRGB(); | 187 inputProfile = qcms_profile_sRGB(); |
| 174 if (!inputProfile) | 188 if (!inputProfile) |
| 175 return; | 189 return nullptr; |
| 190 |
| 191 fprintf(stderr, " from source profile [%s]", qcms_profile_get_descriptio
n(inputProfile)); |
| 192 fprintf(stderr, " to [%s]\n", qcms_profile_get_description(deviceProfile
)); |
| 193 fflush(stderr); |
| 194 |
| 195 // There is no need to create a color transform if the color profiles ma
tch. |
| 196 if (imageColorProfilesEnabled() && qcms_profile_match(inputProfile, devi
ceProfile)) |
| 197 return ColorSpaceProfile::create(inputProfile); |
| 198 |
| 176 // We currently only support color profiles for RGB and RGBA images. | 199 // We currently only support color profiles for RGB and RGBA images. |
| 177 ASSERT(rgbData == qcms_profile_get_color_space(inputProfile)); | 200 ASSERT(rgbData == qcms_profile_get_color_space(inputProfile)); |
| 178 qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_
8; | 201 qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_
8; |
| 179 // FIXME: Don't force perceptual intent if the image profile contains an
intent. | 202 // FIXME: Don't force perceptual intent if the image profile contains an
intent. |
| 180 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProf
ile, dataFormat, QCMS_INTENT_PERCEPTUAL); | 203 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProf
ile, dataFormat, QCMS_INTENT_PERCEPTUAL); |
| 204 if (m_transform) |
| 205 return ColorSpaceProfile::create(inputProfile); |
| 206 |
| 181 qcms_profile_release(inputProfile); | 207 qcms_profile_release(inputProfile); |
| 208 return nullptr; |
| 182 } | 209 } |
| 183 #endif | 210 #endif |
| 184 | 211 |
| 185 private: | 212 private: |
| 186 png_structp m_png; | 213 png_structp m_png; |
| 187 png_infop m_info; | 214 png_infop m_info; |
| 188 PNGImageDecoder* m_decoder; | 215 PNGImageDecoder* m_decoder; |
| 189 unsigned m_readOffset; | 216 unsigned m_readOffset; |
| 190 unsigned m_currentBufferSize; | 217 unsigned m_currentBufferSize; |
| 191 bool m_decodingSizeOnly; | 218 bool m_decodingSizeOnly; |
| 192 bool m_hasAlpha; | 219 bool m_hasAlpha; |
| 193 OwnPtr<png_byte[]> m_interlaceBuffer; | 220 OwnPtr<png_byte[]> m_interlaceBuffer; |
| 194 #if USE(QCMSLIB) | 221 #if USE(QCMSLIB) |
| 195 qcms_transform* m_transform; | 222 qcms_transform* m_transform; |
| 196 OwnPtr<png_byte[]> m_rowBuffer; | 223 OwnPtr<png_byte[]> m_rowBuffer; |
| 197 #endif | 224 #endif |
| 198 }; | 225 }; |
| 199 | 226 |
| 200 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp
tion colorOptions, size_t maxDecodedBytes, unsigned offset) | 227 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp
tion colorOptions, size_t maxDecodedBytes, unsigned offset) |
| 201 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) | 228 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) |
| 202 , m_hasColorProfile(false) | 229 , m_hasColorProfile(false) |
| 203 , m_offset(offset) | 230 , m_offset(offset) |
| 204 { | 231 { |
| 232 fprintf(stderr, "PNG decoder %p created %s\n", this, !isMainThread() ? "impl
-side-thread" : ""); |
| 205 } | 233 } |
| 206 | 234 |
| 207 PNGImageDecoder::~PNGImageDecoder() | 235 PNGImageDecoder::~PNGImageDecoder() |
| 208 { | 236 { |
| 237 fprintf(stderr, "PNG decoder %p ~PNGImageDecoder() %s\n", this, !isMainThrea
d() ? "impl-side-thread" : ""); |
| 209 } | 238 } |
| 210 | 239 |
| 211 #if USE(QCMSLIB) | 240 #if USE(QCMSLIB) |
| 212 static void getColorProfile(png_structp png, png_infop info, ColorProfile& color
Profile, bool& sRGB) | 241 static void getColorProfile(png_structp png, png_infop info, ColorProfile& color
Profile, bool& sRGB) |
| 213 { | 242 { |
| 214 #ifdef PNG_iCCP_SUPPORTED | 243 #ifdef PNG_iCCP_SUPPORTED |
| 215 ASSERT(colorProfile.isEmpty()); | 244 ASSERT(colorProfile.isEmpty()); |
| 216 if (png_get_valid(png, info, PNG_INFO_sRGB)) { | 245 if (png_get_valid(png, info, PNG_INFO_sRGB)) { |
| 217 sRGB = true; | 246 sRGB = true; |
| 218 return; | 247 return; |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 // We only support color profiles for color PALETTE and RGB[A] PNG. Supp
orting | 321 // We only support color profiles for color PALETTE and RGB[A] PNG. Supp
orting |
| 293 // color profiles for gray-scale images is slightly tricky, at least usi
ng the | 322 // color profiles for gray-scale images is slightly tricky, at least usi
ng the |
| 294 // CoreGraphics ICC library, because we expand gray-scale images to RGB
but we | 323 // CoreGraphics ICC library, because we expand gray-scale images to RGB
but we |
| 295 // do not similarly transform the color profile. We'd either need to tra
nsform | 324 // do not similarly transform the color profile. We'd either need to tra
nsform |
| 296 // the color profile or we'd need to decode into a gray-scale image buff
er and | 325 // the color profile or we'd need to decode into a gray-scale image buff
er and |
| 297 // hand that to CoreGraphics. | 326 // hand that to CoreGraphics. |
| 298 bool sRGB = false; | 327 bool sRGB = false; |
| 299 ColorProfile colorProfile; | 328 ColorProfile colorProfile; |
| 300 getColorProfile(png, info, colorProfile, sRGB); | 329 getColorProfile(png, info, colorProfile, sRGB); |
| 301 bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; | 330 bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; |
| 302 m_reader->createColorTransform(colorProfile, imageHasAlpha, sRGB); | 331 RefPtr<ColorSpaceProfile> imageColorProfile = m_reader->createColorTrans
form(colorProfile, imageHasAlpha, sRGB); |
| 303 m_hasColorProfile = !!m_reader->colorTransform(); | 332 m_hasColorProfile = !!imageColorProfile.get(); |
| 333 if (m_hasColorProfile && imageColorProfilesEnabled()) { |
| 334 RELEASE_ASSERT(imageColorProfile->profile()); |
| 335 m_colorProfile = imageColorProfile; |
| 336 } |
| 304 } | 337 } |
| 305 #endif | 338 #endif |
| 306 | 339 |
| 307 if (!m_hasColorProfile) { | 340 if (!m_hasColorProfile) { |
| 308 // Deal with gamma and keep it under our control. | 341 // Deal with gamma and keep it under our control. |
| 309 const double inverseGamma = 0.45455; | 342 const double inverseGamma = 0.45455; |
| 310 const double defaultGamma = 2.2; | 343 const double defaultGamma = 2.2; |
| 311 double gamma; | 344 double gamma; |
| 312 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { | 345 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { |
| 313 const double maxGamma = 21474.83; | 346 const double maxGamma = 21474.83; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 325 if (interlaceType == PNG_INTERLACE_ADAM7) | 358 if (interlaceType == PNG_INTERLACE_ADAM7) |
| 326 png_set_interlace_handling(png); | 359 png_set_interlace_handling(png); |
| 327 | 360 |
| 328 // Update our info now. | 361 // Update our info now. |
| 329 png_read_update_info(png, info); | 362 png_read_update_info(png, info); |
| 330 channels = png_get_channels(png, info); | 363 channels = png_get_channels(png, info); |
| 331 ASSERT(channels == 3 || channels == 4); | 364 ASSERT(channels == 3 || channels == 4); |
| 332 | 365 |
| 333 m_reader->setHasAlpha(channels == 4); | 366 m_reader->setHasAlpha(channels == 4); |
| 334 | 367 |
| 368 fprintf(stderr, "PNG decoder %p headerAvailable %lux%lu %s\n", this, width,
height, m_reader->decodingSizeOnly() ? "size-only-decode" : ""); |
| 369 |
| 335 if (m_reader->decodingSizeOnly()) { | 370 if (m_reader->decodingSizeOnly()) { |
| 336 // If we only needed the size, halt the reader. | 371 // If we only needed the size, halt the reader. |
| 337 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MIN
OR >= 5) | 372 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MIN
OR >= 5) |
| 338 // '0' argument to png_process_data_pause means: Do not cache unprocesse
d data. | 373 // '0' argument to png_process_data_pause means: Do not cache unprocesse
d data. |
| 339 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data
_pause(png, 0)); | 374 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data
_pause(png, 0)); |
| 340 #else | 375 #else |
| 341 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size
); | 376 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size
); |
| 342 png->buffer_size = 0; | 377 png->buffer_size = 0; |
| 343 #endif | 378 #endif |
| 344 } | 379 } |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 png_bytep row = rowBuffer; | 462 png_bytep row = rowBuffer; |
| 428 | 463 |
| 429 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { | 464 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
| 430 unsigned colorChannels = hasAlpha ? 4 : 3; | 465 unsigned colorChannels = hasAlpha ? 4 : 3; |
| 431 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); | 466 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
| 432 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); | 467 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
| 433 } | 468 } |
| 434 | 469 |
| 435 #if USE(QCMSLIB) | 470 #if USE(QCMSLIB) |
| 436 if (qcms_transform* transform = m_reader->colorTransform()) { | 471 if (qcms_transform* transform = m_reader->colorTransform()) { |
| 472 if (rowIndex < 3 || rowIndex >= (unsigned)size().height() - 1) |
| 473 fprintf(stderr, "PNG decoder %p %dx%d color transform row %d\n", thi
s, size().width(), size().height(), rowIndex); |
| 437 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width(
)); | 474 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width(
)); |
| 438 row = m_reader->rowBuffer(); | 475 row = m_reader->rowBuffer(); |
| 439 } | 476 } |
| 440 #endif | 477 #endif |
| 441 | 478 |
| 479 if (rowIndex < 3) |
| 480 fprintf(stderr, "PNG decoder %p %dx%d row %d\n", this, size().width(), s
ize().height(), rowIndex); |
| 481 if (rowIndex >= (unsigned)size().height() - 1) { |
| 482 fprintf(stderr, "PNG decoder %p %dx%d row %d\n", this, size().width(), s
ize().height(), rowIndex); |
| 483 fflush(stderr); |
| 484 } |
| 485 |
| 442 // Write the decoded row pixels to the frame buffer. The repetitive | 486 // Write the decoded row pixels to the frame buffer. The repetitive |
| 443 // form of the row write loops is for speed. | 487 // form of the row write loops is for speed. |
| 444 ImageFrame::PixelData* address = buffer.getAddr(0, y); | 488 ImageFrame::PixelData* address = buffer.getAddr(0, y); |
| 445 unsigned alphaMask = 255; | 489 unsigned alphaMask = 255; |
| 446 int width = size().width(); | 490 int width = size().width(); |
| 447 | 491 |
| 448 png_bytep pixel = row; | 492 png_bytep pixel = row; |
| 449 if (hasAlpha) { | 493 if (hasAlpha) { |
| 450 if (buffer.premultiplyAlpha()) { | 494 if (buffer.premultiplyAlpha()) { |
| 451 for (int x = 0; x < width; ++x, pixel += 4) { | 495 for (int x = 0; x < width; ++x, pixel += 4) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 465 } | 509 } |
| 466 | 510 |
| 467 if (alphaMask != 255 && !buffer.hasAlpha()) | 511 if (alphaMask != 255 && !buffer.hasAlpha()) |
| 468 buffer.setHasAlpha(true); | 512 buffer.setHasAlpha(true); |
| 469 | 513 |
| 470 buffer.setPixelsChanged(true); | 514 buffer.setPixelsChanged(true); |
| 471 } | 515 } |
| 472 | 516 |
| 473 void PNGImageDecoder::complete() | 517 void PNGImageDecoder::complete() |
| 474 { | 518 { |
| 519 fflush(stderr); |
| 520 |
| 475 if (m_frameBufferCache.isEmpty()) | 521 if (m_frameBufferCache.isEmpty()) |
| 476 return; | 522 return; |
| 477 | 523 |
| 478 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); | 524 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); |
| 479 } | 525 } |
| 480 | 526 |
| 481 inline bool isComplete(const PNGImageDecoder* decoder) | 527 inline bool isComplete(const PNGImageDecoder* decoder) |
| 482 { | 528 { |
| 483 return decoder->frameIsCompleteAtIndex(0); | 529 return decoder->frameIsCompleteAtIndex(0); |
| 484 } | 530 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 495 // has failed. | 541 // has failed. |
| 496 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) | 542 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) |
| 497 setFailed(); | 543 setFailed(); |
| 498 | 544 |
| 499 // If decoding is done or failed, we don't need the PNGImageReader anymore. | 545 // If decoding is done or failed, we don't need the PNGImageReader anymore. |
| 500 if (isComplete(this) || failed()) | 546 if (isComplete(this) || failed()) |
| 501 m_reader.clear(); | 547 m_reader.clear(); |
| 502 } | 548 } |
| 503 | 549 |
| 504 } // namespace blink | 550 } // namespace blink |
| OLD | NEW |