| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 #include "config.h" | 29 #include "config.h" |
| 30 #include "platform/image-decoders/webp/WEBPImageDecoder.h" | 30 #include "platform/image-decoders/webp/WEBPImageDecoder.h" |
| 31 | 31 |
| 32 #include "platform/graphics/GraphicsScreen.h" |
| 33 |
| 32 #if USE(QCMSLIB) | 34 #if USE(QCMSLIB) |
| 33 #include "qcms.h" | 35 #include "qcms.h" |
| 34 #endif | 36 #endif |
| 35 | 37 |
| 36 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) | 38 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) |
| 37 #error Blink assumes a little-endian target. | 39 #error Blink assumes a little-endian target. |
| 38 #endif | 40 #endif |
| 39 | 41 |
| 40 #if SK_B32_SHIFT // Output little-endian RGBA pixels (Android). | 42 #if SK_B32_SHIFT // Output little-endian RGBA pixels (Android). |
| 41 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : M
ODE_RGBA; } | 43 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : M
ODE_RGBA; } |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 , m_hasColorProfile(false) | 132 , m_hasColorProfile(false) |
| 131 #if USE(QCMSLIB) | 133 #if USE(QCMSLIB) |
| 132 , m_transform(0) | 134 , m_transform(0) |
| 133 #endif | 135 #endif |
| 134 , m_demux(0) | 136 , m_demux(0) |
| 135 , m_demuxState(WEBP_DEMUX_PARSING_HEADER) | 137 , m_demuxState(WEBP_DEMUX_PARSING_HEADER) |
| 136 , m_haveAlreadyParsedThisData(false) | 138 , m_haveAlreadyParsedThisData(false) |
| 137 , m_repetitionCount(cAnimationLoopOnce) | 139 , m_repetitionCount(cAnimationLoopOnce) |
| 138 , m_decodedHeight(0) | 140 , m_decodedHeight(0) |
| 139 { | 141 { |
| 142 fprintf(stderr, "WEBP decoder %p created %s\n", this, !isMainThread() ? "imp
l-side-thread" : ""); |
| 143 |
| 140 m_blendFunction = (alphaOption == AlphaPremultiplied) ? alphaBlendPremultipl
ied : alphaBlendNonPremultiplied; | 144 m_blendFunction = (alphaOption == AlphaPremultiplied) ? alphaBlendPremultipl
ied : alphaBlendNonPremultiplied; |
| 141 } | 145 } |
| 142 | 146 |
| 143 WEBPImageDecoder::~WEBPImageDecoder() | 147 WEBPImageDecoder::~WEBPImageDecoder() |
| 144 { | 148 { |
| 145 clear(); | 149 clear(); |
| 146 } | 150 } |
| 147 | 151 |
| 148 void WEBPImageDecoder::clear() | 152 void WEBPImageDecoder::clear() |
| 149 { | 153 { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 // Repetition count is the number of animation cycles to show, | 235 // Repetition count is the number of animation cycles to show, |
| 232 // where 0 means "infinite". But ImageSource::repetitionCount() | 236 // where 0 means "infinite". But ImageSource::repetitionCount() |
| 233 // returns -1 for "infinite", and 0 and up for "show the image | 237 // returns -1 for "infinite", and 0 and up for "show the image |
| 234 // animation one cycle more than the value". Subtract one here | 238 // animation one cycle more than the value". Subtract one here |
| 235 // to correctly handle the finite and infinite cases. | 239 // to correctly handle the finite and infinite cases. |
| 236 --m_repetitionCount; | 240 --m_repetitionCount; |
| 237 // FIXME: Implement ICC profile support for animated images. | 241 // FIXME: Implement ICC profile support for animated images. |
| 238 m_formatFlags &= ~ICCP_FLAG; | 242 m_formatFlags &= ~ICCP_FLAG; |
| 239 } | 243 } |
| 240 | 244 |
| 245 fprintf(stderr, "WEBP decoder %p headerAvailable %dx%d %s\n", this, size
().width(), size().height(), "size-only-decode"); |
| 246 |
| 241 #if USE(QCMSLIB) | 247 #if USE(QCMSLIB) |
| 242 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) | 248 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) |
| 243 readColorProfile(); | 249 readColorProfile(); |
| 244 #endif | 250 #endif |
| 245 } | 251 } |
| 246 | 252 |
| 247 ASSERT(isDecodedSizeAvailable()); | 253 ASSERT(isDecodedSizeAvailable()); |
| 248 return true; | 254 return true; |
| 249 } | 255 } |
| 250 | 256 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 | 315 |
| 310 #if USE(QCMSLIB) | 316 #if USE(QCMSLIB) |
| 311 | 317 |
| 312 void WEBPImageDecoder::clearColorTransform() | 318 void WEBPImageDecoder::clearColorTransform() |
| 313 { | 319 { |
| 314 if (m_transform) | 320 if (m_transform) |
| 315 qcms_transform_release(m_transform); | 321 qcms_transform_release(m_transform); |
| 316 m_transform = 0; | 322 m_transform = 0; |
| 317 } | 323 } |
| 318 | 324 |
| 319 bool WEBPImageDecoder::createColorTransform(const char* data, size_t size) | 325 PassRefPtr<ColorSpaceProfile> WEBPImageDecoder::createColorTransform(const char*
data, size_t size) |
| 320 { | 326 { |
| 321 clearColorTransform(); | 327 clearColorTransform(); |
| 322 | 328 |
| 329 fprintf(stderr, "WEBP decoder %p createColorTransform ", this); |
| 330 |
| 331 if (m_deviceProfile) |
| 332 fprintf(stderr, ": device %p\n", m_deviceProfile.get()); |
| 333 else |
| 334 fprintf(stderr, ": %p\n", nullptr); |
| 335 fprintf(stderr, "image color profiles enabled: %d\n", imageColorProfilesEnab
led()); |
| 336 fflush(stderr); |
| 337 |
| 323 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); | 338 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); |
| 339 if (m_deviceProfile) |
| 340 deviceProfile = m_deviceProfile->profile(); |
| 324 if (!deviceProfile) | 341 if (!deviceProfile) |
| 325 return false; | 342 return nullptr; |
| 343 |
| 326 qcms_profile* inputProfile = qcms_profile_from_memory(data, size); | 344 qcms_profile* inputProfile = qcms_profile_from_memory(data, size); |
| 327 if (!inputProfile) | 345 if (!inputProfile) |
| 328 return false; | 346 return nullptr; |
| 347 |
| 348 fprintf(stderr, " from source profile [%s]", qcms_profile_get_description(in
putProfile)); |
| 349 fprintf(stderr, " to [%s]\n", qcms_profile_get_description(deviceProfile)); |
| 350 fflush(stderr); |
| 351 |
| 352 // There is no need to create a color transform if the color profiles match. |
| 353 if (imageColorProfilesEnabled() && qcms_profile_match(inputProfile, devicePr
ofile)) |
| 354 return ColorSpaceProfile::create(inputProfile); |
| 329 | 355 |
| 330 // We currently only support color profiles for RGB profiled images. | 356 // We currently only support color profiles for RGB profiled images. |
| 331 ASSERT(rgbData == qcms_profile_get_color_space(inputProfile)); | 357 ASSERT(rgbData == qcms_profile_get_color_space(inputProfile)); |
| 332 // The input image pixels are RGBA format. | 358 // The input image pixels are RGBA format. |
| 333 qcms_data_type format = QCMS_DATA_RGBA_8; | 359 qcms_data_type format = QCMS_DATA_RGBA_8; |
| 334 // FIXME: Don't force perceptual intent if the image profile contains an int
ent. | 360 // FIXME: Don't force perceptual intent if the image profile contains an int
ent. |
| 335 m_transform = qcms_transform_create(inputProfile, format, deviceProfile, QCM
S_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL); | 361 m_transform = qcms_transform_create(inputProfile, format, deviceProfile, QCM
S_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL); |
| 362 if (m_transform) |
| 363 return ColorSpaceProfile::create(inputProfile); |
| 336 | 364 |
| 337 qcms_profile_release(inputProfile); | 365 qcms_profile_release(inputProfile); |
| 338 return !!m_transform; | 366 return nullptr; |
| 339 } | 367 } |
| 340 | 368 |
| 341 void WEBPImageDecoder::readColorProfile() | 369 void WEBPImageDecoder::readColorProfile() |
| 342 { | 370 { |
| 343 WebPChunkIterator chunkIterator; | 371 WebPChunkIterator chunkIterator; |
| 344 if (!WebPDemuxGetChunk(m_demux, "ICCP", 1, &chunkIterator)) { | 372 if (!WebPDemuxGetChunk(m_demux, "ICCP", 1, &chunkIterator)) { |
| 373 RELEASE_ASSERT(!m_hasColorProfile && !m_transform); |
| 345 WebPDemuxReleaseChunkIterator(&chunkIterator); | 374 WebPDemuxReleaseChunkIterator(&chunkIterator); |
| 346 return; | 375 return; |
| 347 } | 376 } |
| 348 | 377 |
| 349 const char* profileData = reinterpret_cast<const char*>(chunkIterator.chunk.
bytes); | 378 const char* profileData = reinterpret_cast<const char*>(chunkIterator.chunk.
bytes); |
| 350 size_t profileSize = chunkIterator.chunk.size; | 379 size_t profileSize = chunkIterator.chunk.size; |
| 351 | 380 |
| 352 // Only accept RGB color profiles from input class devices. | 381 // Only accept RGB color profiles from input class devices. |
| 353 bool ignoreProfile = false; | 382 bool ignoreProfile = false; |
| 354 if (profileSize < ImageDecoder::iccColorProfileHeaderLength) | 383 if (profileSize < ImageDecoder::iccColorProfileHeaderLength) |
| 355 ignoreProfile = true; | 384 ignoreProfile = true; |
| 356 else if (!ImageDecoder::rgbColorProfile(profileData, profileSize)) | 385 else if (!ImageDecoder::rgbColorProfile(profileData, profileSize)) |
| 357 ignoreProfile = true; | 386 ignoreProfile = true; |
| 358 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileSize)) | 387 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileSize)) |
| 359 ignoreProfile = true; | 388 ignoreProfile = true; |
| 360 | 389 |
| 361 if (!ignoreProfile) | 390 if (ignoreProfile) { |
| 362 m_hasColorProfile = createColorTransform(profileData, profileSize); | 391 RELEASE_ASSERT(!m_hasColorProfile && !m_transform); |
| 392 WebPDemuxReleaseChunkIterator(&chunkIterator); |
| 393 return; |
| 394 } |
| 395 |
| 396 RefPtr<ColorSpaceProfile> imageColorProfile = createColorTransform(profileDa
ta, profileSize); |
| 397 m_hasColorProfile = !!imageColorProfile.get(); |
| 398 if (m_hasColorProfile && imageColorProfilesEnabled()) { |
| 399 RELEASE_ASSERT(imageColorProfile->profile()); |
| 400 m_colorProfile = imageColorProfile; |
| 401 } |
| 363 | 402 |
| 364 WebPDemuxReleaseChunkIterator(&chunkIterator); | 403 WebPDemuxReleaseChunkIterator(&chunkIterator); |
| 365 } | 404 } |
| 366 | 405 |
| 367 #endif // USE(QCMSLIB) | 406 #endif // USE(QCMSLIB) |
| 368 | 407 |
| 369 void WEBPImageDecoder::applyPostProcessing(size_t frameIndex) | 408 void WEBPImageDecoder::applyPostProcessing(size_t frameIndex) |
| 370 { | 409 { |
| 371 ImageFrame& buffer = m_frameBufferCache[frameIndex]; | 410 ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
| 372 int width; | 411 int width; |
| 373 int decodedHeight; | 412 int decodedHeight; |
| 374 if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, 0)) | 413 if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, 0)) |
| 375 return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 | 414 return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 |
| 376 if (decodedHeight <= 0) | 415 if (decodedHeight <= 0) |
| 377 return; | 416 return; |
| 378 | 417 |
| 379 const IntRect& frameRect = buffer.originalFrameRect(); | 418 const IntRect& frameRect = buffer.originalFrameRect(); |
| 380 ASSERT_WITH_SECURITY_IMPLICATION(width == frameRect.width()); | 419 ASSERT_WITH_SECURITY_IMPLICATION(width == frameRect.width()); |
| 381 ASSERT_WITH_SECURITY_IMPLICATION(decodedHeight <= frameRect.height()); | 420 ASSERT_WITH_SECURITY_IMPLICATION(decodedHeight <= frameRect.height()); |
| 382 const int left = frameRect.x(); | 421 const int left = frameRect.x(); |
| 383 const int top = frameRect.y(); | 422 const int top = frameRect.y(); |
| 384 | 423 |
| 424 if (m_decodedHeight < decodedHeight) |
| 425 fprintf(stderr, "WEBP decoder %p %dx%d decode row %d\n", this, size().wi
dth(), size().height(), decodedHeight); |
| 426 |
| 385 #if USE(QCMSLIB) | 427 #if USE(QCMSLIB) |
| 386 if (qcms_transform* transform = colorTransform()) { | 428 if (qcms_transform* transform = colorTransform()) { |
| 429 if (m_decodedHeight < decodedHeight) |
| 430 fprintf(stderr, "WEBP decoder %p %dx%d color transform row %d\n", th
is, size().width(), size().height(), decodedHeight); |
| 387 for (int y = m_decodedHeight; y < decodedHeight; ++y) { | 431 for (int y = m_decodedHeight; y < decodedHeight; ++y) { |
| 388 const int canvasY = top + y; | 432 const int canvasY = top + y; |
| 389 uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(left, canva
sY)); | 433 uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(left, canva
sY)); |
| 390 qcms_transform_data_type(transform, row, row, width, QCMS_OUTPUT_RGB
X); | 434 qcms_transform_data_type(transform, row, row, width, QCMS_OUTPUT_RGB
X); |
| 391 uint8_t* pixel = row; | 435 uint8_t* pixel = row; |
| 392 for (int x = 0; x < width; ++x, pixel += 4) { | 436 for (int x = 0; x < width; ++x, pixel += 4) { |
| 393 const int canvasX = left + x; | 437 const int canvasX = left + x; |
| 394 buffer.setRGBA(canvasX, canvasY, pixel[0], pixel[1], pixel[2], p
ixel[3]); | 438 buffer.setRGBA(canvasX, canvasY, pixel[0], pixel[1], pixel[2], p
ixel[3]); |
| 395 } | 439 } |
| 396 } | 440 } |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 537 } | 581 } |
| 538 | 582 |
| 539 m_decoderBuffer.u.RGBA.rgba = reinterpret_cast<uint8_t*>(buffer.getAddr(fram
eRect.x(), frameRect.y())); | 583 m_decoderBuffer.u.RGBA.rgba = reinterpret_cast<uint8_t*>(buffer.getAddr(fram
eRect.x(), frameRect.y())); |
| 540 | 584 |
| 541 switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { | 585 switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { |
| 542 case VP8_STATUS_OK: | 586 case VP8_STATUS_OK: |
| 543 applyPostProcessing(frameIndex); | 587 applyPostProcessing(frameIndex); |
| 544 buffer.setHasAlpha((m_formatFlags & ALPHA_FLAG) || m_frameBackgroundHasA
lpha); | 588 buffer.setHasAlpha((m_formatFlags & ALPHA_FLAG) || m_frameBackgroundHasA
lpha); |
| 545 buffer.setStatus(ImageFrame::FrameComplete); | 589 buffer.setStatus(ImageFrame::FrameComplete); |
| 546 clearDecoder(); | 590 clearDecoder(); |
| 591 fflush(stderr); |
| 547 return true; | 592 return true; |
| 548 case VP8_STATUS_SUSPENDED: | 593 case VP8_STATUS_SUSPENDED: |
| 549 if (!isAllDataReceived() && !frameIsCompleteAtIndex(frameIndex)) { | 594 if (!isAllDataReceived() && !frameIsCompleteAtIndex(frameIndex)) { |
| 550 applyPostProcessing(frameIndex); | 595 applyPostProcessing(frameIndex); |
| 596 fflush(stderr); |
| 551 return false; | 597 return false; |
| 552 } | 598 } |
| 553 // FALLTHROUGH | 599 // FALLTHROUGH |
| 554 default: | 600 default: |
| 555 clear(); | 601 clear(); |
| 556 return setFailed(); | 602 return setFailed(); |
| 557 } | 603 } |
| 558 } | 604 } |
| 559 | 605 |
| 560 } // namespace blink | 606 } // namespace blink |
| OLD | NEW |