| 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 | 
|---|